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