/* unixio.c */ #include "ztypes.h" #if defined(BSD) #include #endif /* defined(BSD) */ #if defined(SYSTEM_FIVE) #include #endif /* defined(SYSTEM_FIVE) */ #if defined(POSIX) #include #endif /* defined(POSIX) */ #include #include #include #if defined(JOHN) #include "john.h" #endif /* needed by AIX */ #if defined(AIX) #include #endif extern int hist_buf_size; /* new stuff for command editing */ int BUFFER_SIZE; char *commands; int space_avail; static int ptr1, ptr2 = 0; static int end_ptr = 0; static int row, head_col; static int keypad_avail=1; /* done with editing global info */ static int current_row = 1; static int current_col = 1; static int saved_row; static int saved_col; static int cursor_saved = OFF; static char tcbuf[1024]; static char cmbuf[1024]; static char *cmbufp; static char *CE, *CL, *CM, *CS, *DL, *MD, *ME, *MR, *SE, *SO, *TE, *TI, *UE, *US, *KD, *KL, *KR, *KU; #define GET_TC_STR(p1, p2) if ((p1 = tgetstr (p2, &cmbufp)) == NULL) p1 = "" #define BELL 7 static void outc (); static void display_string (); static int wait_for_char (); static int read_key (); static void set_cbreak_mode (); static void rundown (); /* static void get_prev_command (); static void get_next_command (); static void get_first_command (); static void delete_command (); static void add_command (); */ /* done with editing prototypes */ extern int tgetent (); extern int tgetnum (); extern char *tgetstr (); extern char *tgoto (); extern void tputs (); static void outc (c) int c; { putchar (c); }/* outc */ void initialize_screen () { char *term; int row, col; if ((term = getenv ("TERM")) == NULL) fatal ("No TERM environment variable"); if (tgetent (tcbuf, term) <= 0) fatal ("No termcap entry for this terminal"); cmbufp = cmbuf; /* COMMENTS to aid in porting to other sys. */ GET_TC_STR (CE, "ce"); /* clear to end of line */ GET_TC_STR (CL, "cl"); /* clear screen, home cursor */ GET_TC_STR (CM, "cm"); /* screen-rel cursor motion to row m, col n */ GET_TC_STR (CS, "cs"); /* change scroll region to lines m thru n */ GET_TC_STR (DL, "dl"); /* delete line */ GET_TC_STR (MD, "md"); /* turn on bold attribute */ GET_TC_STR (ME, "me"); /* turn off all attributes */ GET_TC_STR (MR, "mr"); /* turn on reverse video attribute */ GET_TC_STR (SE, "se"); /* end standout mode */ GET_TC_STR (SO, "so"); /* begin standout mode */ GET_TC_STR (TE, "te"); /* string to end a termcap program */ GET_TC_STR (TI, "ti"); /* string to begin a termcap program */ GET_TC_STR (UE, "ue"); /* end underscore mode */ GET_TC_STR (US, "us"); /* begin underscore mode */ GET_TC_STR (KU, "ku"); /* sent by keypad up arrow */ GET_TC_STR (KD, "kd"); /* sent by keypad down arrow */ GET_TC_STR (KL, "kl"); /* sent by keypad left arrow */ GET_TC_STR (KR, "kr"); /* sent by keypad right arrow */ if (screen_cols == 0 && (screen_cols = tgetnum ("co")) == -1) screen_cols = DEFAULT_COLS; if (screen_rows == 0 && (screen_rows = tgetnum ("li")) == -1) screen_rows = DEFAULT_ROWS; if (*KU == '\0' || *KD == '\0' || *KL == '\0' || *KR =='\0') { keypad_avail=0; } if (*MD == '\0' || *ME == '\0' || *MR == '\0') { MD = SO; ME = SE; MR = SO; } if (*UE == '\0' || *US == '\0') { UE = SE; US = SO; } tputs (TI, 1, outc); clear_screen (); row = screen_rows / 2 - 1; col = (screen_cols - (sizeof (JZIPVER) - 1)) / 2; move_cursor (row, col); display_string (JZIPVER); row = screen_rows / 2; col = (screen_cols - (sizeof ("The story is loading...") - 1)) / 2; move_cursor (row, col); display_string ("The story is loading..."); h_interpreter = INTERP_DEC_20; JTERP = INTERP_UNIX; commands = (char *)malloc(hist_buf_size*sizeof(char)); if (commands==NULL) fatal("Could not allocate history buffer."); BUFFER_SIZE=hist_buf_size; space_avail = hist_buf_size-1; /* I have code the does analysis of the command buffer. I have */ /* to insure that it will work by initing the buffer to known values. */ #if defined(JOHN) init_commands(); #endif set_cbreak_mode (1); interp_initialized = 1; }/* initialize_screen */ void restart_screen () { cursor_saved = OFF; if (h_type < V4) set_byte (H_CONFIG, (get_byte (H_CONFIG) | CONFIG_WINDOWS)); else set_byte (H_CONFIG, (get_byte (H_CONFIG) | CONFIG_EMPHASIS | CONFIG_WINDOWS)); /* Force graphics off as we can't do them */ set_word (H_FLAGS, (get_word (H_FLAGS) & (~GRAPHICS_FLAG))); }/* restart_screen */ void reset_screen () { /* only do this stuff on exit when called AFTER initialize_screen */ if (interp_initialized) { delete_status_window (); select_text_window (); set_attribute (NORMAL); set_cbreak_mode (0); tputs (TE, 1, outc); #if defined JOHN command_stats(); #endif } }/* reset_screen */ void clear_screen () { tputs (CL, 1, outc); current_row = 1; current_col = 1; }/* clear_screen */ void select_status_window () { save_cursor_position (); }/* select_status_window */ void select_text_window () { restore_cursor_position (); }/* select_text_window */ void create_status_window () { int row, col; if (*CS) { get_cursor_position (&row, &col); tputs (tgoto (CS, screen_rows - 1, status_size), 1, outc); move_cursor (row, col); } }/* create_status_window */ void delete_status_window () { int row, col; if (*CS) { get_cursor_position (&row, &col); tputs (tgoto (CS, screen_rows - 1, 0), 1, outc); move_cursor (row, col); } }/* delete_status_window */ void clear_line () { tputs (CE, 1, outc); }/* clear_line */ void clear_text_window () { int i, row, col; get_cursor_position (&row, &col); for (i = status_size + 1; i <= screen_rows; i++) { move_cursor (i, 1); clear_line (); } move_cursor (row, col); }/* clear_text_window */ void clear_status_window () { int i, row, col; get_cursor_position (&row, &col); for (i = status_size; i; i--) { move_cursor (i, 1); clear_line (); } move_cursor (row, col); }/* clear_status_window */ void move_cursor (row, col) int row; int col; { tputs (tgoto (CM, col - 1, row - 1), 1, outc); current_row = row; current_col = col; }/* move_cursor */ void get_cursor_position (row, col) int *row; int *col; { *row = current_row; *col = current_col; }/* get_cursor_position */ void save_cursor_position () { if (cursor_saved == OFF) { get_cursor_position (&saved_row, &saved_col); cursor_saved = ON; } }/* save_cursor_position */ void restore_cursor_position () { if (cursor_saved == ON) { move_cursor (saved_row, saved_col); cursor_saved = OFF; } }/* restore_cursor_position */ void set_attribute (attribute) int attribute; { if (attribute == NORMAL) { tputs (ME, 1, outc); tputs (UE, 1, outc); } if (attribute & REVERSE) tputs (MR, 1, outc); if (attribute & BOLD) tputs (MD, 1, outc); if (attribute & EMPHASIS) tputs (US, 1, outc); if (attribute & FIXED_FONT) ; }/* set_attribute */ static void display_string (s) char *s; { while (*s) display_char (*s++); }/* display_string */ void display_char (c) int c; { outc (c); if (++current_col > screen_cols) current_col = screen_cols; }/* display_char */ void scroll_line () { int row, col; get_cursor_position (&row, &col); if (*CS || row < screen_rows) { display_char ('\n'); } else { move_cursor (status_size + 1, 1); tputs (DL, 1, outc); move_cursor (row, 1); } current_col = 1; if (++current_row > screen_rows) current_row = screen_rows; }/* scroll_line */ /* * Previous command system * * Here's how this works: * * The previous command buffer is BUFFER_SIZE bytes long. After the player * presses Enter, the command is added to this buffer, with a trailing '\n' * added. The '\n' is used to show where one command ends and another begins. * * The up arrow key retrieves a previous command. This is done by working * backwards through the buffer until a '\n' is found. The down arrow * retieves the next command by counting forward. The ptr1 and ptr2 * values hold the start and end of the currently displayed command. * * PgUp displays the first ("oldest") command, while PgDn displays a blank * prompt. */ int display_command (buffer) char *buffer; { int counter, loop; move_cursor(row, head_col); clear_line(); /* ptr1 = end_ptr when the player has selected beyond any previously * saved command. */ if (ptr1 == end_ptr) { return(0); } else { /* Put the characters from the save buffer into the variable "buffer". * The return value (counter) is the value of *read_size. */ counter = 0; for (loop = ptr1; loop <= ptr2; loop++) { buffer[counter] = commands[loop]; display_char(buffer[counter++]); } return(counter); } } /* display_command */ void get_prev_command () { /* Checking to see if ptr1 > 0 prevents moving ptr1 and ptr2 into * never-never land. */ if (ptr1 > 0) { /* Subtract 2 to jump over any intervening '\n' */ ptr2 = ptr1 -= 2; /* If we've jumped too far, fix it */ if (ptr1 < 0) ptr1 = 0; if (ptr2 < 0) ptr2 = 0; if (ptr1 > 0) { do /* Decrement ptr1 until a '\n' is found */ ptr1--; while ((commands[ptr1] != '\n') && (ptr1 >= 0)); /* Then advance back to the position after the '\n' */ ptr1++; } } } /* get_prev_command */ void get_next_command () { if (ptr2 < end_ptr) { /* Add 2 to advance over any intervening '\n' */ ptr1 = ptr2 += 2; if (ptr2 >= end_ptr) { ptr1 = ptr2 = end_ptr; } else { do ptr2++; while ((commands[ptr2] != '\n') && (ptr2 <= end_ptr)); ptr2--; } } } /* get_next_command */ void get_first_command () { if (end_ptr > 1) { ptr1 = ptr2 = 0; do ptr2++; while (commands[ptr2] != '\n'); ptr2--; } } /* get_first_command */ void delete_command () { /* Deletes entire commands from the beginning of the command buffer */ int loop; /* Keep moving the characters in the command buffer one space to the left * until a '\n' is found... */ do { for (loop = 0; loop < end_ptr; loop++) { commands[loop] = commands[loop + 1]; } end_ptr--; space_avail++; } while (commands[0] != '\n'); /* ...then delete the '\n' */ for (loop = 0; loop < end_ptr; loop++) { commands[loop] = commands[loop + 1]; } end_ptr--; space_avail++; ptr1 = ptr2 = end_ptr; } /* delete_command */ void add_command (buffer, size) char *buffer; int size; { int loop, counter; /* Add the player's last command to the command buffer */ counter = 0; for (loop = end_ptr; loop < (end_ptr + size); loop++) { commands[loop] = buffer[counter++]; } /* Add one space for '\n' */ end_ptr += size + 1; ptr1 = ptr2 = end_ptr; commands[end_ptr - 1] = '\n'; space_avail -= size + 1; } /* add_command */ int input_line (buflen, buffer, timeout, read_size) int buflen; char *buffer; int timeout; int *read_size; { struct timeval tv; struct timezone tz; int c, col; int init_char_pos, curr_char_pos; int loop, tail_col; int keyfunc=0; /* * init_char_pos : the initial cursor location * curr_char_pos : the current character position within the input line * head_col: the head of the input line (used for cursor position) * (global variable) * tail_col: the end of the input line (used for cursor position) */ get_cursor_position (&row, &col); head_col = tail_col = col; init_char_pos = curr_char_pos = *read_size; ptr1 = ptr2 = end_ptr; /* if (timeout != 0) { gettimeofday (&tv, &tz); tv.tv_usec += timeout*100000; while (tv.tv_usec>1000000) { tv.tv_sec++; tv.tv_usec -= 1000000; } } */ for ( ; ; ) { keyfunc=0; /* Read a single keystroke */ fflush (stdout); /* if (timeout && wait_for_char (&tv)) */ if (timeout!=0) { if ( wait_for_char (timeout)) return (-1); } c = read_key (); /****** Previous Command Selection Keys ******/ if (keypad_avail) { if (c == (char) *KU ) { for (loop=0; loop<(strlen(KU)-1); loop++) c=getchar(); if (c == (unsigned char) KU[strlen(KU)-1]) { /* Up arrow */ get_prev_command(); curr_char_pos = *read_size = display_command(buffer); tail_col = head_col + *read_size; keyfunc=1; } else if (c == (unsigned char) KD[strlen(KU)-1]) { /* Down arrow */ get_next_command(); curr_char_pos = *read_size = display_command(buffer); tail_col = head_col + *read_size; keyfunc=1; } /* PgUp else if (c == (unsigned char) '\x09a') { get_first_command(); curr_char_pos = *read_size = display_command(buffer); tail_col = head_col + *read_size; keyfunc=1; } */ /* PgDn else if (c == (unsigned char) '\x094') { ptr1 = ptr2 = end_ptr; curr_char_pos = *read_size = display_command(buffer); tail_col = head_col + *read_size; keyfunc=1; } */ /****** Cursor Editing Keys ******/ else if (c == (unsigned char) KL[strlen(KL)-1]) { /* Left arrow */ get_cursor_position (&row, &col); /* Prevents moving the cursor into the prompt */ if (col > head_col) { move_cursor (row, --col); curr_char_pos--; } keyfunc=1; } else if (c == (unsigned char) KR[strlen(KR)-1]) { /* Right arrow */ get_cursor_position (&row, &col); /* Prevents moving the cursor beyond the end of the input line */ if (col < tail_col) { move_cursor (row, ++col); curr_char_pos++; } keyfunc=1; } /* End else if (c == (unsigned char) '\x092') { move_cursor(row, tail_col); curr_char_pos = init_char_pos + *read_size; keyfunc=1; } */ /* Home else if (c == (unsigned char) '\x098') { move_cursor(row, head_col); curr_char_pos = init_char_pos; keyfunc=1; } */ /* Delete */ /* else if (c == (unsigned char) '\x096') { if (curr_char_pos < *read_size) { get_cursor_position (&row, &col); for (loop = curr_char_pos; loop < *read_size; loop++) { buffer[loop] = buffer[loop + 1]; } tail_col--; (*read_size)--; for (loop = curr_char_pos; loop < *read_size; loop++) { display_char (buffer[loop]); } display_char (' '); move_cursor(row, col); keyfunc=1; } } */ } } if (!keyfunc) { if (c == '\b') { /* Backspace */ get_cursor_position (&row, &col); if (col > head_col) { move_cursor (row, --col); for (loop = curr_char_pos; loop < *read_size; loop++) { buffer[loop - 1] = buffer[loop]; display_char (buffer[loop - 1]); } display_char (' '); curr_char_pos--; tail_col--; (*read_size)--; move_cursor(row, col); } } else { /* Normal key action */ if (*read_size == (buflen - 1)) { /* Ring bell if buffer is full */ outc (BELL); } else { /* Scroll line if return key pressed */ if (c == '\r' || c == '\n') { c = '\n'; scroll_line (); } if (c == '\n') { /* Add the current command to the command buffer */ if (*read_size > space_avail) { do delete_command(); while (*read_size > space_avail); } if (*read_size > 0) add_command(buffer, *read_size); /* Return key if it is a line terminator */ return (c); } else { get_cursor_position (&row, &col); /* Used if the cursor is not at the end of the line */ if (col < tail_col) { /* Moves the input line one character to the right */ for (loop = *read_size; loop >= curr_char_pos; loop--) { buffer[loop+1] = buffer[loop]; } /* Puts the character into the space created by the * "for" loop above */ buffer[curr_char_pos] = (char) c; /* Increment the end of the line values */ (*read_size)++; tail_col++; /* Move the cursor back to its original position */ move_cursor(row, col); /* Redisplays the input line from the point of * insertion */ for (loop = curr_char_pos; loop < *read_size; loop++) { display_char (buffer[loop]); } /* Moves the cursor to the next position */ move_cursor(row, ++col); curr_char_pos++; } else /* Used if the cursor is at the end of the line */ { buffer[curr_char_pos++] = (char) c; display_char (c); (*read_size)++; tail_col++; } } } } } } }/* input_line */ /* * Patched 28-June-1995: Changed this routine's expectation of a \n to * a \r so the form in Bureaucracy works. Patch * applied by John Holder. */ int input_character (timeout) int timeout; { struct timeval tv; struct timezone tz; int c; /* gettimeofday (&tv, &tz); tv.tv_sec += timeout; */ fflush (stdout); /* if (timeout && wait_for_char (&tv)) */ if (timeout!=0) { if( wait_for_char (timeout)) return (-1); } c = read_key (); if ( c == '\n' ) c = '\r'; return (c); }/* input_character */ /* timeout is in tenths of a second */ static int wait_for_char (timeout) /* struct timeval *timeout; */ int timeout; { int nfds, status; fd_set readfds; struct timeval tv; struct timezone tz; gettimeofday (&tv, &tz); /* if (tv.tv_sec >= timeout->tv_sec && tv.tv_usec >= timeout->tv_usec) return (-1); */ if (timeout==0) return(-1); /* tv.tv_sec = timeout->tv_sec - tv.tv_sec; if (timeout->tv_usec < tv.tv_usec) { tv.tv_sec--; tv.tv_usec = (timeout->tv_usec + 1000000) - tv.tv_usec; } else tv.tv_usec = timeout->tv_usec - tv.tv_usec; */ tv.tv_sec = (timeout*100000)/1000000; tv.tv_usec = (timeout*100000)%1000000; /* used to be the following, but some boxen have no getdtablesize() */ /* nfds = getdtablesize (); */ nfds = FD_SETSIZE; FD_ZERO (&readfds); FD_SET (fileno (stdin), &readfds); status = select (nfds, &readfds, NULL, NULL, &tv); if (status < 0) { perror ("select"); return (-1); } if (status == 0) return (-1); else return (0); }/* wait_for_char */ static int read_key () { int c; c = getchar (); if (c == 127) c = '\b'; else if (c == '\r') c = '\n'; return (c); }/* read_key */ static void set_cbreak_mode (mode) int mode; { int status; #if defined(BSD) struct sgttyb new_tty; static struct sgttyb old_tty; #endif /* defined(BSD) */ #if defined(SYSTEM_FIVE) struct termio new_termio; static struct termio old_termio; #endif /* defined(SYSTEM_FIVE) */ #if defined(POSIX) struct termios new_termios; static struct termios old_termios; #endif /* defined(POSIX) */ #if defined(BSD) status = ioctl (fileno (stdin), (mode) ? TIOCGETP : TIOCSETP, &old_tty); #endif /* defined(BSD) */ #if defined(SYSTEM_FIVE) status = ioctl (fileno (stdin), (mode) ? TCGETA : TCSETA, &old_termio); #endif /* defined(SYSTEM_FIVE) */ #if defined(POSIX) if (mode) status = tcgetattr (fileno (stdin), &old_termios); else status = tcsetattr (fileno (stdin), TCSANOW, &old_termios); #endif /* defined(POSIX) */ if (status) { perror ("ioctl"); exit (1); } if (mode) { signal (SIGINT, rundown); signal (SIGTERM, rundown); } if (mode) { #if defined(BSD) status = ioctl (fileno (stdin), TIOCGETP, &new_tty); #endif /* defined(BSD) */ #if defined(SYSTEM_FIVE) status = ioctl (fileno (stdin), TCGETA, &new_termio); #endif /* defined(SYSTEM_FIVE) */ #if defined(POSIX) status = tcgetattr (fileno (stdin), &new_termios); #endif /* defined(POSIX) */ if (status) { perror ("ioctl"); exit (1); } #if defined(BSD) new_tty.sg_flags |= CBREAK; new_tty.sg_flags &= ~ECHO; #endif /* defined(BSD) */ #if defined(SYSTEM_FIVE) new_termio.c_lflag &= ~(ICANON | ECHO); #endif /* defined(SYSTEM_FIVE) */ #if defined(POSIX) new_termios.c_lflag &= ~(ICANON | ECHO); /* the next two lines of code added by Mark Phillips. The patch */ /* was for sun and __hpux, active only if those were #defined, */ /* but most POSIX boxen (SunOS, HP-UX, Dec OSF, Irix for sure) */ /* can use this... It makes character input work. VMIN and */ /* VTIME are reused on some systems, so when the mode is switched*/ /* to RAW all character access is, by default, buffered wrong. */ /* For the curious: VMIN='\004' and VTIME='\005' octal on */ /* these systems. VMIN is usually EOF and VTIME is EOL. (JDH) */ new_termios.c_cc[VMIN]=1; new_termios.c_cc[VTIME]=2; #endif /* defined(POSIX) */ #if defined(BSD) status = ioctl (fileno (stdin), TIOCSETP, &new_tty); #endif /* defined(BSD) */ #if defined(SYSTEM_FIVE) status = ioctl (fileno (stdin), TCSETA, &new_termio); #endif /* defined(SYSTEM_FIVE) */ #if defined(POSIX) status = tcsetattr (fileno (stdin), TCSANOW, &new_termios); #endif /* defined(POSIX) */ if (status) { perror ("ioctl"); exit (1); } } if (mode == 0) { signal (SIGINT, SIG_DFL); signal (SIGTERM, SIG_DFL); } }/* set_cbreak_mode */ static void rundown () { unload_cache (); close_story (); close_script (); reset_screen (); }/* rundown */ #if defined HARD_COLORS void set_colours (foreground, background) int foreground; int background; { int fg, bg; /* int colour_map[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; */ if (foreground!=0) { fg = (foreground == 1 || foreground>9) ? 7 : foreground - 2; } if (background!=0) { bg = (background == 1 || background>9) ? 4 : background - 2; } printf("[3%d;4%dm", fg, bg); } #endif