|
|
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.