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