bph@buengc.BU.EDU (Blair P. Houghton) (07/22/88)
Thanks much to Jim Gettys for posting vrogue to the net; once I found the bugs (they were communications-related, not in the program itself; i.e., all of the escape characters had been stripped out by someone's repeater...I #defined escape in its octal format, and switched to static arrays for the strings.) the program worked wondrously; I know it's impossible, but it even seems _faster_ with vrogue in the pipe...probably just crisper since things are now handled using buffers instead of character-at-a-time. THERE ARE STILL BUGS IN IT. They're just not the kind that will keep me from using the game, now. Specifically, o when monsters are highlighted in other rooms (it would be a spoiler to say how), the redraw skips some spaces; it only lasts for the few turns that the monsters are highlighted. o something in the state machine is unable to recognize when the tombstone is being printed out, hence the tombstone ends up being partially in graphics characters. If this bugs you, use the options menu to turn the tombstone off, or fix the bug and repost. Barring any glitches from transmission, the code below should work. Jim suggested using the alias xterm -fn rogue-n -fb rogue-b -e vrogue & to get it running. More elegant hacks include color and geometry specifications, of course, but be sure to use at least 80 columns by 24 lines, or rogue will barf. My favorite colors are amber background and blue foreground (add "-fg blue -bg amber" *before* the "-e"). --Blair "does this get me my comp.unix.wizards membership button and decoder ring?" -----------------------Clip Here----------------------------- #include <signal.h> #include <stdio.h> #include <sgtty.h> #include <sys/wait.h> #include <sys/time.h> #define BUFLEN 1024 #define ESCAPE '\033' int got_a_signal; main(argc,argv) int argc; char *argv[]; { extern int tstp_handler (), quit_handler (), chld_handler (), int_handler (); extern int got_a_signal; struct ltchars new_ltc, old_ltc; struct sigvec vec; int topipe[2], frompipe[2]; int pipeflag, nfds, from_rogue, to_rogue, readfds; int num, rogue_pid, old_mask; /* Since this IS a game, reduce our priority. */ setpriority(0, getpid(), 4); /* Create the pipes that will connect us to rogue. */ if (pipe (topipe) != 0) { printf ("Couldn't open pipe to rogue\n"); exit (1); } if (pipe (frompipe) != 0) { printf ("Couldn't open pipe to rogue\n"); exit (1); } if (rogue_pid = vfork ()) { /* Close the ends of the pipes that we don't need, and make better names for the ones we do. */ close (topipe[0]); close (frompipe[1]); from_rogue = frompipe[0]; to_rogue = topipe[1]; /* Get the old interrupt chars and save them. */ if (ioctl (0, TIOCGLTC, &old_ltc)) printf("Couldn't get old_ltc.\n"); /* Copy the old interrupt chars and make the rest of them correspond to what rogue uses. */ new_ltc = old_ltc; new_ltc.t_dsuspc = -1; if (ioctl (0, TIOCSLTC, &new_ltc)) printf("Couldn't remove ^Y.\n"); /* Install our interrupt handlers */ vec.sv_mask = 0; vec.sv_onstack = 0; vec.sv_handler = tstp_handler; if (sigvec (SIGTSTP, &vec, 0)) printf("Couldn't install TSTP handler.\n"); vec.sv_handler = quit_handler; if (sigvec (SIGQUIT, &vec, 0)) printf("Couldn't install QUIT handler.\n"); vec.sv_handler = int_handler; if (sigvec (SIGINT, &vec, 0)) printf("Couldn't install INT handler.\n"); vec.sv_handler = chld_handler; if (sigvec (SIGCHLD, &vec, 0)) printf("Couldn't install CHLD handler.\n"); pipeflag = 1 << from_rogue; nfds = from_rogue + 1; while (1) { readfds = pipeflag | 1; num = select (nfds, &readfds, 0, 0, 0); if (num > 0) if (readfds & pipeflag) process_output(from_rogue); else process_input(to_rogue); if (got_a_signal) { handle_signal(got_a_signal, rogue_pid, from_rogue, &old_ltc); got_a_signal = 0; } } } else { /* We are the child process, so attach our ends of the pipe to stdin and stdout. */ if ((dup2 (topipe[0], 0)) == -1) { printf ("couldn't dup2 stdin pipe\n\n"); exit (1); } if ((dup2 (frompipe[1], 1)) == -1) { printf ("couldn't dup2 stdout pipe\n\n"); exit (1); } /* Close the ends of the pipe we don't need. */ close (topipe[1]); close (frompipe[0]); /* Do it. */ execv ("/usr/games/rogue", argv); printf ("Couldn't execv rogue!\n"); exit (1); } } handle_signal(the_signal, rogue_pid, from_rogue, old_ltc) int the_signal, rogue_pid, from_rogue; struct ltchars *old_ltc; { switch (the_signal) { case SIGQUIT: if (kill(rogue_pid, SIGQUIT)) { normal_font(); printf("Couldn't QUIT rogue!\n"); exit(1); } break; case SIGTSTP: if (kill(rogue_pid, SIGTSTP)) { normal_font(); printf("Couldn't TSTP rogue!\n"); exit(1); } break; case SIGINT: if (kill(rogue_pid, SIGINT)) { normal_font(); printf("Couldn't INT rogue!\n"); exit(1); } break; case SIGCHLD: { union wait status; int nfds, pipeflag; int num, readfds; struct timeval timeout; /* For some reason, our child has stopped. There is no need (I think) to send it any more characters, so just finish processing its output, and decide how we should stop. */ nfds = from_rogue + 1; pipeflag = 1 << from_rogue; timeout.tv_sec = 0; timeout.tv_usec = 100000; do { readfds = pipeflag; num = select (nfds, &readfds, 0, 0, &timeout); if (num > 0) num = process_output(from_rogue); } while(num > 0); normal_font(); if (wait3(&status, WNOHANG | WUNTRACED, 0) > 0) { if (WIFSTOPPED(status)) { if (kill(getpid(), SIGSTOP)) { printf("Couldn't STOP myself!\n"); exit(1); } if (kill(rogue_pid, SIGCONT)) { printf("Couldn't CONT rogue!\n"); exit(1); } } else if (WIFEXITED(status)) { ioctl(0,TIOCSLTC,old_ltc); exit(status.w_retcode); } else if (WIFSIGNALED(status)) { ioctl(0,TIOCSLTC,old_ltc); printf("Rogue terminated.\n"); printf("termsig: %d\n",status.w_termsig); printf("coredump: %d\n",status.w_coredump); printf("retcode: %d\n",status.w_retcode); exit(-1); } else { ioctl(0,TIOCSLTC,old_ltc); printf("Rogue's status is bizzare!\n"); exit(1); } } } break; default: break; } } quit_handler() { extern int got_a_signal; got_a_signal = SIGQUIT; } tstp_handler() { extern int got_a_signal; got_a_signal = SIGTSTP; } int_handler() { extern int got_a_signal; got_a_signal = SIGINT; } chld_handler() { extern int got_a_signal; got_a_signal = SIGCHLD; } normal_font() { static char normal_code[4] = {ESCAPE,'[','m','\0'}; write(1, normal_code, 3); } special_font() { static char special_code[5] = {ESCAPE,'[','1','m','\0'}; write(1, special_code, 4); } int process_input(to_rogue) int to_rogue; { static char buf[BUFLEN]; register int num, old_mask; old_mask = sigblock(1 << (SIGCHLD - 1)); num = read (0, buf, BUFLEN); if (num <= 0) { printf("Error reading keyboard!\n"); exit(1); } write (to_rogue, buf, num); sigsetmask(old_mask); return(num); } int process_output(from_rogue) int from_rogue; { static char buf[BUFLEN]; static int state = 0; static int linenum = 1; static int special = 0; static int force = 0; static int accum = 0; int chr, i, start, num, something_changed, old_mask; old_mask = sigblock(1 << (SIGCHLD - 1)); num = read (from_rogue, buf, BUFLEN); if (num <= 0) { sigsetmask(old_mask); return(num); } start = 0; for (i = 0; i < num; i++) { chr = buf[i]; switch (state) { case 0: /* State 0 is the normal idle state. */ switch (chr) { case ESCAPE: state = 1; break; case '\n': linenum++; something_changed = 1; break; case 'a': case 'b': case 'c': case 'd': /* e is left out on purpose */ case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': if ((linenum > 1) && (linenum < 24) && (!force)) { force = 1; something_changed = 1; } break; case 'e': if (force) { state = 5; } if ((linenum > 1) && (linenum < 24) && (!force)) { state = 5; force = 1; something_changed = 1; } break; default: break; } break; case 1: /* We've begun an escape sequence. */ switch (chr) { case '[': state = 2; break; default: state = 0; break; } break; case 2: /* We've seen "<esc>[" */ switch (chr) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': state = 3; accum = chr - '0'; break; case ';': state = 4; accum = 1; break; case 'A': state = 0; linenum--; something_changed = 1; break; case 'H': state = 0; linenum = 1; something_changed = 1; break; case 'm': /* We've seen "<esc>[m" Rogue is trying to get out of reverse video. */ state = 0; break; default: state = 0; break; } break; case 3: /* We've seen "<esc>[n" Continue parsing the argument. */ switch (chr) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': accum = (10 * accum) + chr - '0'; break; case ';': state = 4; break; case 'm': /* Rogue is trying to use reverse video or something. */ state = 0; break; default: state = 0; break; } break; case 4: /* We've seen "<esc>[nn;" Ignore the rest of the digits until the magic H completes the sequence. */ switch (chr) { case 'H': state = 0; linenum = accum; something_changed = 1; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': break; default: state = 0; break; } break; case 5: /* We've seen an "e" on lines 2-23. This means that we're in an inventory or something. We're hoping for "e--", the end of "--more--" or "--Press space to continue--". */ switch (chr) { case '-': state = 6; break; case ESCAPE: state = 1; break; case '\n': state = 0; linenum++; something_changed = 1; break; default: break; } break; case 6: /* We've seen "e-". If we see another "-" we can stop forcing. */ switch (chr) { case '-': state = 0; force = 0; something_changed = 1; break; case ESCAPE: state = 1; break; case '\n': state = 0; linenum++; something_changed = 1; break; default: break; } break; default: break; } if (something_changed) { something_changed = 0; if (force) { /* Since we're just starting for force, force before outputting the character just looked at. */ if (special) { write(1, buf + start, i - start); normal_font(); start = i; special = 0; } } else if ((linenum == 1) || (linenum >= 24)) { if (special) { write (1, buf + start, i - start + 1); normal_font(); start = i + 1; special = 0; } } else { if (!special) { write (1, buf + start, i - start + 1); special_font(); start = i + 1; special = 1; } } } } /* Output any characters remaining in the buffer. */ if (i != start) write (1, buf + start, i - start); sigsetmask(old_mask); return(num); }