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