file reorg
[unix-history] / usr / src / games / rogue / machdep.c
CommitLineData
fc3e88fd
KB
1/*
2 * Copyright (c) 1988 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Timothy C. Stoehr.
7 *
8 * Redistribution and use in source and binary forms are permitted
9 * provided that the above copyright notice and this paragraph are
10 * duplicated in all such forms and that any documentation,
11 * advertising materials, and other materials related to such
12 * distribution and use acknowledge that the software was developed
13 * by the University of California, Berkeley. The name of the
14 * University may not be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 */
20
21#ifndef lint
22static char sccsid[] = "@(#)machdep.c 5.3 (Berkeley) %G%";
23#endif /* not lint */
24
b3afadef
KB
25/*
26 * machdep.c
27 *
28 * This source herein may be modified and/or distributed by anybody who
29 * so desires, with the following restrictions:
30 * 1.) No portion of this notice shall be removed.
31 * 2.) Credit shall not be taken for the creation of this source.
32 * 3.) This code is not to be traded, sold, or used for personal
33 * gain or profit.
34 *
35 */
36
b3afadef
KB
37/* Included in this file are all system dependent routines. Extensive use
38 * of #ifdef's will be used to compile the appropriate code on each system:
39 *
40 * UNIX: all UNIX systems.
41 * UNIX_BSD4_2: UNIX BSD 4.2 and later, UTEK, (4.1 BSD too?)
42 * UNIX_SYSV: UNIX system V
43 * UNIX_V7: UNIX version 7
44 *
45 * All UNIX code should be included between the single "#ifdef UNIX" at the
46 * top of this file, and the "#endif" at the bottom.
47 *
48 * To change a routine to include a new UNIX system, simply #ifdef the
49 * existing routine, as in the following example:
50 *
51 * To make a routine compatible with UNIX system 5, change the first
52 * function to the second:
53 *
54 * md_function()
55 * {
56 * code;
57 * }
58 *
59 * md_function()
60 * {
61 * #ifdef UNIX_SYSV
62 * sys5code;
63 * #else
64 * code;
65 * #endif
66 * }
67 *
68 * Appropriate variations of this are of course acceptible.
69 * The use of "#elseif" is discouraged because of non-portability.
70 * If the correct #define doesn't exist, "UNIX_SYSV" in this case, make it up
71 * and insert it in the list at the top of the file. Alter the CFLAGS
72 * in you Makefile appropriately.
73 *
74 */
75
76#ifdef UNIX
77
78#include <stdio.h>
79#include <sys/types.h>
80#include <sys/file.h>
81#include <sys/stat.h>
23d68be9 82#include <pwd.h>
b3afadef
KB
83
84#ifdef UNIX_BSD4_2
85#include <sys/time.h>
86#include <sgtty.h>
87#endif
88
89#ifdef UNIX_SYSV
90#include <time.h>
91#include <termio.h>
92#endif
93
94#include <signal.h>
95#include "rogue.h"
96
97/* md_slurp:
98 *
99 * This routine throws away all keyboard input that has not
100 * yet been read. It is used to get rid of input that the user may have
101 * typed-ahead.
102 *
103 * This function is not necessary, so it may be stubbed. The might cause
104 * message-line output to flash by because the game has continued to read
105 * input without waiting for the user to read the message. Not such a
106 * big deal.
107 */
108
109md_slurp()
110{
111 long ln = 0;
112
113#ifdef UNIX_BSD4_2
114 ioctl(0, FIONREAD, &ln);
115#endif
116#ifdef UNIX_SYSV
117 ioctl(0, TCFLSH, &ln);
118 ln = 0;
119#endif
120
121 ln += stdin->_cnt;
122
123 for (; ln > 0; ln--) {
124 (void) getchar();
125 }
126}
127
128/* md_control_keyboard():
129 *
130 * This routine is much like md_cbreak_no_echo_nonl() below. It sets up the
131 * keyboard for appropriate input. Specifically, it prevents the tty driver
132 * from stealing characters. For example, ^Y is needed as a command
133 * character, but the tty driver intercepts it for another purpose. Any
134 * such behavior should be stopped. This routine could be avoided if
135 * we used RAW mode instead of CBREAK. But RAW mode does not allow the
136 * generation of keyboard signals, which the program uses.
137 *
138 * The parameter 'mode' when true, indicates that the keyboard should
139 * be set up to play rogue. When false, it should be restored if
140 * necessary.
141 *
142 * This routine is not strictly necessary and may be stubbed. This may
143 * cause certain command characters to be unavailable.
144 */
145
146md_control_keybord(mode)
147boolean mode;
148{
149 static boolean called_before = 0;
150#ifdef UNIX_BSD4_2
151 static struct ltchars ltc_orig;
152 static struct tchars tc_orig;
153 struct ltchars ltc_temp;
154 struct tchars tc_temp;
155#endif
156#ifdef UNIX_SYSV
157 static struct termio _oldtty;
158 struct termio _tty;
159#endif
160
161 if (!called_before) {
162 called_before = 1;
163#ifdef UNIX_BSD4_2
164 ioctl(0, TIOCGETC, &tc_orig);
165 ioctl(0, TIOCGLTC, &ltc_orig);
166#endif
167#ifdef UNIX_SYSV
168 ioctl(0, TCGETA, &_oldtty);
169#endif
170 }
171#ifdef UNIX_BSD4_2
172 ltc_temp = ltc_orig;
173 tc_temp = tc_orig;
174#endif
175#ifdef UNIX_SYSV
176 _tty = _oldtty;
177#endif
178
179 if (!mode) {
180#ifdef UNIX_BSD4_2
181 ltc_temp.t_suspc = ltc_temp.t_dsuspc = -1;
182 ltc_temp.t_rprntc = ltc_temp.t_flushc = -1;
183 ltc_temp.t_werasc = ltc_temp.t_lnextc = -1;
184 tc_temp.t_startc = tc_temp.t_stopc = -1;
185#endif
186#ifdef UNIX_SYSV
187 _tty.c_cc[VSWTCH] = CNSWTCH;
188#endif
189 }
190#ifdef UNIX_BSD4_2
191 ioctl(0, TIOCSETC, &tc_temp);
192 ioctl(0, TIOCSLTC, &ltc_temp);
193#endif
194#ifdef UNIX_SYSV
195 ioctl(0, TCSETA, &_tty);
196#endif
197}
198
199/* md_heed_signals():
200 *
201 * This routine tells the program to call particular routines when
202 * certain interrupts/events occur:
203 *
204 * SIGINT: call onintr() to interrupt fight with monster or long rest.
205 * SIGQUIT: call byebye() to check for game termination.
206 * SIGHUP: call error_save() to save game when terminal hangs up.
207 *
208 * On VMS, SIGINT and SIGQUIT correspond to ^C and ^Y.
209 *
210 * This routine is not strictly necessary and can be stubbed. This will
211 * mean that the game cannot be interrupted properly with keyboard
212 * input, this is not usually critical.
213 */
214
215md_heed_signals()
216{
217 signal(SIGINT, onintr);
218 signal(SIGQUIT, byebye);
219 signal(SIGHUP, error_save);
220}
221
222/* md_ignore_signals():
223 *
224 * This routine tells the program to completely ignore the events mentioned
225 * in md_heed_signals() above. The event handlers will later be turned on
226 * by a future call to md_heed_signals(), so md_heed_signals() and
227 * md_ignore_signals() need to work together.
228 *
229 * This function should be implemented or the user risks interrupting
230 * critical sections of code, which could cause score file, or saved-game
231 * file, corruption.
232 */
233
234md_ignore_signals()
235{
236 signal(SIGQUIT, SIG_IGN);
237 signal(SIGINT, SIG_IGN);
238 signal(SIGHUP, SIG_IGN);
239}
240
241/* md_get_file_id():
242 *
243 * This function returns an integer that uniquely identifies the specified
244 * file. It need not check for the file's existence. In UNIX, the inode
245 * number is used.
246 *
247 * This function is used to identify saved-game files.
248 */
249
250int
251md_get_file_id(fname)
252char *fname;
253{
254 struct stat sbuf;
255
256 if (stat(fname, &sbuf)) {
257 return(-1);
258 }
259 return((int) sbuf.st_ino);
260}
261
262/* md_link_count():
263 *
264 * This routine returns the number of hard links to the specified file.
265 *
266 * This function is not strictly necessary. On systems without hard links
267 * this routine can be stubbed by just returning 1.
268 */
269
270int
271md_link_count(fname)
272char *fname;
273{
274 struct stat sbuf;
275
276 stat(fname, &sbuf);
277 return((int) sbuf.st_nlink);
278}
279
280/* md_gct(): (Get Current Time)
281 *
282 * This function returns the current year, month(1-12), day(1-31), hour(0-23),
283 * minute(0-59), and second(0-59). This is used for identifying the time
284 * at which a game is saved.
285 *
286 * This function is not strictly necessary. It can be stubbed by returning
287 * zeros instead of the correct year, month, etc. If your operating
288 * system doesn't provide all of the time units requested here, then you
289 * can provide only those that it does, and return zeros for the others.
290 * If you cannot provide good time values, then users may be able to copy
291 * saved-game files and play them.
292 */
293
294md_gct(rt_buf)
295struct rogue_time *rt_buf;
296{
297 struct tm *t, *localtime();
298 long seconds;
299
300 time(&seconds);
301 t = localtime(&seconds);
302
303 rt_buf->year = t->tm_year;
304 rt_buf->month = t->tm_mon + 1;
305 rt_buf->day = t->tm_mday;
306 rt_buf->hour = t->tm_hour;
307 rt_buf->minute = t->tm_min;
308 rt_buf->second = t->tm_sec;
309}
310
311/* md_gfmt: (Get File Modification Time)
312 *
313 * This routine returns a file's date of last modification in the same format
314 * as md_gct() above.
315 *
316 * This function is not strictly necessary. It is used to see if saved-game
317 * files have been modified since they were saved. If you have stubbed the
318 * routine md_gct() above by returning constant values, then you may do
319 * exactly the same here.
320 * Or if md_gct() is implemented correctly, but your system does not provide
321 * file modification dates, you may return some date far in the past so
322 * that the program will never know that a saved-game file being modified.
323 * You may also do this if you wish to be able to restore games from
324 * saved-games that have been modified.
325 */
326
327md_gfmt(fname, rt_buf)
328char *fname;
329struct rogue_time *rt_buf;
330{
331 struct stat sbuf;
332 long seconds;
333 struct tm *t;
334
335 stat(fname, &sbuf);
336 seconds = (long) sbuf.st_mtime;
337 t = localtime(&seconds);
338
339 rt_buf->year = t->tm_year;
340 rt_buf->month = t->tm_mon + 1;
341 rt_buf->day = t->tm_mday;
342 rt_buf->hour = t->tm_hour;
343 rt_buf->minute = t->tm_min;
344 rt_buf->second = t->tm_sec;
345}
346
347/* md_df: (Delete File)
348 *
349 * This function deletes the specified file, and returns true (1) if the
350 * operation was successful. This is used to delete saved-game files
351 * after restoring games from them.
352 *
353 * Again, this function is not strictly necessary, and can be stubbed
354 * by simply returning 1. In this case, saved-game files will not be
355 * deleted and can be replayed.
356 */
357
358boolean
359md_df(fname)
360char *fname;
361{
362 if (unlink(fname)) {
363 return(0);
364 }
365 return(1);
366}
367
368/* md_gln: (Get login name)
369 *
370 * This routine returns the login name of the user. This string is
371 * used mainly for identifying users in score files.
372 *
373 * A dummy string may be returned if you are unable to implement this
374 * function, but then the score file would only have one name in it.
375 */
376
377char *
378md_gln()
379{
23d68be9 380 struct passwd *p, *getpwuid();
b3afadef 381
23d68be9
KB
382 if (!(p = getpwuid(getuid())))
383 return((char *)NULL);
384 return(p->pw_name);
b3afadef
KB
385}
386
387/* md_sleep:
388 *
389 * This routine causes the game to pause for the specified number of
390 * seconds.
391 *
392 * This routine is not particularly necessary at all. It is used for
393 * delaying execution, which is useful to this program at some times.
394 */
395
396md_sleep(nsecs)
397int nsecs;
398{
399 (void) sleep(nsecs);
400}
401
402/* md_getenv()
403 *
404 * This routine gets certain values from the user's environment. These
405 * values are strings, and each string is identified by a name. The names
406 * of the values needed, and their use, is as follows:
407 *
408 * TERMCAP
409 * The name of the users's termcap file, NOT the termcap entries
410 * themselves. This is used ONLY if the program is compiled with
411 * CURSES defined (-DCURSES). Even in this case, the program need
412 * not find a string for TERMCAP. If it does not, it will use the
413 * default termcap file as returned by md_gdtcf();
414 * TERM
415 * The name of the users's terminal. This is used ONLY if the program
416 * is compiled with CURSES defined (-DCURSES). In this case, the string
417 * value for TERM must be found, or the routines in curses.c cannot
418 * function, and the program will quit.
419 * ROGUEOPTS
420 * A string containing the various game options. This need not be
421 * defined.
422 * HOME
423 * The user's home directory. This is only used when the user specifies
424 * '~' as the first character of a saved-game file. This string need
425 * not be defined.
426 * SHELL
427 * The user's favorite shell. If not found, "/bin/sh" is assumed.
428 *
429 * If your system does not provide a means of searching for these values,
430 * you will have to do it yourself. None of the values above really need
431 * to be defined except TERM when the program is compiled with CURSES
432 * defined. In this case, as a bare minimum, you can check the 'name'
433 * parameter, and if it is "TERM" find the terminal name and return that,
434 * else return zero. If the program is not compiled with CURSES, you can
435 * get by with simply always returning zero. Returning zero indicates
436 * that their is no defined value for the given string.
437 */
438
439char *
440md_getenv(name)
441char *name;
442{
443 char *value;
444 char *getenv();
445
446 value = getenv(name);
447
448 return(value);
449}
450
451/* md_malloc()
452 *
453 * This routine allocates, and returns a pointer to, the specified number
454 * of bytes. This routines absolutely MUST be implemented for your
455 * particular system or the program will not run at all. Return zero
456 * when no more memory can be allocated.
457 */
458
459char *
460md_malloc(n)
461int n;
462{
463 char *malloc();
464 char *t;
465
466 t = malloc(n);
467 return(t);
468}
469
470/* md_gseed() (Get Seed)
471 *
472 * This function returns a seed for the random number generator (RNG). This
473 * seed causes the RNG to begin generating numbers at some point in it's
474 * sequence. Without a random seed, the RNG will generate the same set
475 * of numbers, and every game will start out exactly the same way. A good
476 * number to use is the process id, given by getpid() on most UNIX systems.
477 *
478 * You need to find some single random integer, such as:
479 * process id.
480 * current time (minutes + seconds) returned from md_gct(), if implemented.
481 *
482 * It will not help to return "get_rand()" or "rand()" or the return value of
483 * any pseudo-RNG. If you don't have a random number, you can just return 1,
484 * but this means your games will ALWAYS start the same way, and will play
485 * exactly the same way given the same input.
486 */
487
488md_gseed()
489{
490 return(getpid());
491}
492
493/* md_exit():
494 *
495 * This function causes the program to discontinue execution and exit.
496 * This function must be implemented or the program will continue to
497 * hang when it should quit.
498 */
499
500md_exit(status)
501int status;
502{
503 exit(status);
504}
505
506/* md_lock():
507 *
508 * This function is intended to give the user exclusive access to the
509 * score file. It does so by "creat"ing a lock file, which can only
510 * be created if it does not already exist. The file is deleted when
511 * score file processing is finished. The lock file should be located
512 * in the same directory as the score file. These full path names should
513 * be defined for any particular site in rogue.h. The constants SCORE_FILE
514 * and LOCK_FILE define these file names.
515 *
516 * When the parameter 'l' is non-zero (true), a lock is requested. Otherwise
517 * the lock is released by removing the lock file.
518 */
519
520md_lock(l)
521boolean l;
522{
523 short tries;
524 char *lock_file = LOCK_FILE;
525
526 if (l) {
527 for (tries = 0; tries < 5; tries++) {
528 if (md_get_file_id(lock_file) == -1) {
529 if (creat(lock_file, 0444) != -1) {
530 break;
531 } else {
532 message("cannot lock score file", 0);
533 }
534 } else {
535 message("waiting to lock score file", 0);
536 }
537 sleep(2);
538 }
539 } else {
540 (void) unlink(lock_file);
541 }
542}
543
544/* md_shell():
545 *
546 * This function spawns a shell for the user to use. When this shell is
547 * terminated, the game continues. Since this program may often be run
548 * setuid to gain access to privileged files, care is taken that the shell
549 * is run with the user's REAL user id, and not the effective user id.
550 * The effective user id is restored after the shell completes.
551 */
552
553md_shell(shell)
554char *shell;
555{
556 long w[2];
557
558 if (!fork()) {
559 int uid;
560
561 uid = getuid();
562 setuid(uid);
563 execl(shell, shell, 0);
564 }
565 wait(w);
566}
567
568/* If you have a viable curses/termlib library, then use it and don't bother
569 * implementing the routines below. And don't compile with -DCURSES.
570 */
571
572#ifdef CURSES
573
574/* md_cbreak_no_echo_nonl:
575 *
576 * This routine sets up some terminal characteristics. The tty-driver
577 * must be told to:
578 * 1.) Not echo input.
579 * 2.) Transmit input characters immediately upon typing. (cbreak mode)
580 * 3.) Move the cursor down one line, without changing column, and
581 * without generating a carriage-return, when it
582 * sees a line-feed. This is only necessary if line-feed is ever
583 * used in the termcap 'do' (cursor down) entry, in which case,
584 * your system should must have a way of accomplishing this.
585 *
586 * When the parameter 'on' is true, the terminal is set up as specified
587 * above. When this parameter is false, the terminal is restored to the
588 * original state.
589 *
590 * Raw mode should not to be used. Keyboard signals/events/interrupts should
591 * be sent, although they are not strictly necessary. See notes in
592 * md_heed_signals().
593 *
594 * This function must be implemented for rogue to run properly if the
595 * program is compiled with CURSES defined to use the enclosed curses
596 * emulation package. If you are not using this, then this routine is
597 * totally unnecessary.
598 *
599 * Notice that information is saved between calls. This is used to
600 * restore the terminal to an initial saved state.
601 *
602 */
603
604md_cbreak_no_echo_nonl(on)
605boolean on;
606{
607#ifdef UNIX_BSD4_2
608 static struct sgttyb tty_buf;
609 static int tsave_flags;
610
611 if (on) {
612 ioctl(0, TIOCGETP, &tty_buf);
613 tsave_flags = tty_buf.sg_flags;
614 tty_buf.sg_flags |= CBREAK;
615 tty_buf.sg_flags &= ~(ECHO | CRMOD); /* CRMOD: see note 3 above */
616 ioctl(0, TIOCSETP, &tty_buf);
617 } else {
618 tty_buf.sg_flags = tsave_flags;
619 ioctl(0, TIOCSETP, &tty_buf);
620 }
621#endif
622#ifdef UNIX_SYSV
623 struct termio tty_buf;
624 static struct termio tty_save;
625
626 if (on) {
627 ioctl(0, TCGETA, &tty_buf);
628 tty_save = tty_buf;
629 tty_buf.c_lflag &= ~(ICANON | ECHO);
630 tty_buf.c_oflag &= ~ONLCR;
631 tty_buf.c_cc[4] = 1; /* MIN */
632 tty_buf.c_cc[5] = 2; /* TIME */
633 ioctl(0, TCSETAF, &tty_buf);
634 } else {
635 ioctl(0, TCSETAF, &tty_save);
636 }
637#endif
638}
639
640/* md_gdtcf(): (Get Default Termcap File)
641 *
642 * This function is called ONLY when the program is compiled with CURSES
643 * defined. If you use your system's curses/termlib library, this function
644 * won't be called. On most UNIX systems, "/etc/termcap" suffices.
645 *
646 * If their is no such termcap file, then return 0, but in that case, you
647 * must have a TERMCAP file returned from md_getenv("TERMCAP"). The latter
648 * will override the value returned from md_gdtcf(). If the program is
649 * compiled with CURSES defined, and md_gdtcf() returns 0, and
650 * md_getenv("TERMCAP") returns 0, the program will have no terminal
651 * capability information and will quit.
652 */
653
654char *
655md_gdtcf()
656{
657 return("/etc/termcap");
658}
659
660/* md_tstp():
661 *
662 * This function puts the game to sleep and returns to the shell. This
663 * only applies to UNIX 4.2 and 4.3. For other systems, the routine should
664 * be provided as a do-nothing routine. md_tstp() will only be referenced
665 * in the code when compiled with CURSES defined.
666 *
667 */
668
669md_tstp()
670{
671#ifdef UNIX_BSD4_2
672 kill(0, SIGTSTP);
673#endif
674}
675
676#endif
677
678#endif