/* * txInput.c -- * * Handles 'stdin' and terminal driver settings. * * ********************************************************************* * * Copyright (C) 1985, 1990 Regents of the University of California. * * * Permission to use, copy, modify, and distribute this * * * software and its documentation for any purpose and without * * * fee is hereby granted, provided that the above copyright * * * notice appear in all copies. The University of California * * * makes no representations about the suitability of this * * * software for any purpose. It is provided "as is" without * * * express or implied warranty. Export of this software outside * * * of the United States of America may require an export license. * * ********************************************************************* */ #ifndef lint static char rcsid[]="$Header: /ufs/repository/magic/lisp/other/txInput.c,v 1.1.1.1 2000/03/29 18:35:56 rajit Exp $"; #endif #include #include #include "magsgtty.h" #include "magic.h" #include "magsgtty.h" #include "textio.h" #include "geometry.h" #include "txcommands.h" #include "textioInt.h" #include "dqueue.h" #include "graphics.h" /* Characters for input processing. Set to -1 if not defined */ char txEraseChar = -1; /* Erase line (e.g. ^H) */ char txKillChar = -1; /* Kill line (e.g. ^U or ^X) */ char txWordChar = -1; /* Erase a word (e.g. ^W) */ char txReprintChar = -1; /* Reprint the line (e.g. ^R */ char txLitChar = -1; /* Literal next character (e.g. ^V */ char txBreakChar = -1; /* Break to a new line (normally -1) */ char TxEOFChar = -1; /* The current EOF character (e.g. ^D) */ char TxInterruptChar = -1; /* The current interrupt character (e.g. ^C) */ static char txPromptChar; /* the current prompt */ bool txHavePrompt = FALSE; /* is a prompt on the screen? */ char *txReprint1 = NULL; char *txReprint2 = NULL; /* * ---------------------------------------------------------------------------- * * TxReprint -- * * Reprint the current input line. Used for ^R and after ^Z. * * Results: * None. * * Side Effects: * None. * * ---------------------------------------------------------------------------- */ void TxReprint() { (void) txFprintfBasic(stdout, "\n"); if (txReprint1 != NULL) (void) txFprintfBasic(stdout, "%s", txReprint1); if (txReprint2 != NULL) (void) txFprintfBasic(stdout, "%s", txReprint2); (void) fflush(stdout); } /* * ---------------------------------------------------------------------------- * TxSetPrompt -- * * Set the current prompt. * * Results: * None. * * Side effects: * You can guess. * ---------------------------------------------------------------------------- */ void TxSetPrompt(ch) char ch; { txPromptChar = ch; } /* * ---------------------------------------------------------------------------- * TxPrompt -- * * Put up the prompt which was set by TxSetPrompt. * * Results: * None. * * Side effects: * You can guess. * ---------------------------------------------------------------------------- */ void TxPrompt() { static char lastPromptChar; static char prompts[2]; if (txHavePrompt && (lastPromptChar == txPromptChar)) return; (void) fflush(stderr); if (txHavePrompt) TxUnPrompt(); if (TxInteractive) txFprintfBasic(stdout, "%c", txPromptChar); (void) fflush(stdout); prompts[0] = txPromptChar; prompts[1] = '\0'; txReprint1 = prompts; txHavePrompt = TRUE; lastPromptChar = txPromptChar; } /* * ---------------------------------------------------------------------------- * TxRestorePrompt -- * * The prompt was erased for some reason. Restore it. * * Results: * None. * * Side effects: * You can guess. * ---------------------------------------------------------------------------- */ void TxRestorePrompt() { if (txHavePrompt) { txHavePrompt = FALSE; TxPrompt(); } } /* * ---------------------------------------------------------------------------- * TxUnPrompt -- * * Erase the prompt. * * Results: * None. * * Side effects: * You can guess. * ---------------------------------------------------------------------------- */ void TxUnPrompt() { if (txHavePrompt) { (void) fflush(stderr); if (TxInteractive) fputs("\b \b", stdout); (void) fflush(stdout); txReprint1 = NULL; txHavePrompt = FALSE; } } /* * ---------------------------------------------------------------------------- * * TxGetChar -- * * Get a single character from the input stream (terminal or whatever). * * Results: * One character, or EOF. * * Side Effects: * None. * * ---------------------------------------------------------------------------- */ int TxGetChar() { int ch; extern DQueue txInputEvents, txFreeEvents; extern TxInputEvent txLastEvent; TxInputEvent *event; while (TRUE) { /* Get an input character. Don't let TxGetInputEvent return * because of a SigWinch or whatever. It would be nice to process * SigWinches in a middle of text input, but that is difficult * to do since SigWinches are processed in the normal * command-interpretation process. Thus, we delay the processing * until after the text is collected. */ if (DQIsEmpty(&txInputEvents)) TxGetInputEvent(TRUE, FALSE); event = (TxInputEvent *) DQPopFront(&txInputEvents); ASSERT(event != NULL, "TxGetChar"); txLastEvent = *event; if (event->txe_button == TX_EOF) { ch = EOF; goto gotone; } if (event->txe_button == TX_CHARACTER) { ch = event->txe_ch; goto gotone; } DQPushRear(&txFreeEvents, (ClientData) event); } gotone: DQPushRear(&txFreeEvents, (ClientData) event); return ch; } /* * ---------------------------------------------------------------------------- * * TxGetLinePrompt -- * * Just like the following TxGetLine proc, but it prompts first. It * is an advantage to have this proc put out the prompt, since that * way if the user suspends Magic and then continues it the prompt will * reappear in the proper fashion. * * Results: * A char pointer or NULL (see TxGetLine). * * Side Effects: * None. * * ---------------------------------------------------------------------------- */ #ifndef NO_READLINE_SUPPORT #include "readline/readline.h" #include "readline/history.h" #endif char * TxGetLinePrompt(dest, maxChars, prompt) char *dest; int maxChars; char *prompt; { char *res; if (txHavePrompt) TxUnPrompt(); #ifdef NO_READLINE_SUPPORT /* without GNU readline */ if (prompt != NULL) TxPrintf("%s", prompt); txReprint1 = prompt; res = TxGetLine(dest, maxChars); txReprint1 = NULL; #else rl_getc_function = TxGetChar; TxResetTerminal (); if (prompt != NULL) res = readline (prompt); else res = readline (":"); if (res && *res) add_history (res); if (res) { if (strlen (res) >= maxChars) { TxPrintf ("WARNING: input string too long; truncated"); res[maxChars-1] = '\0'; } strcpy (dest, res); #undef free free (res); #define free You_should_use_the_Magic_procedure_freeMagic res = dest; } TxSetTerminal (); #endif return res; } /* * ---------------------------------------------------------------------------- * TxGetLine: * * Reads a line from the input queue (terminal or whatever). * * Results: * A char pointer to the string is returned. * If an end-of-file is typed, returns (char *) NULL and * stores in the string any characters recieved up to that point. * * Side effects: * The input stream is read, and 'dest' is filled in with up to maxChars-1 * characters. Up to maxChars of the 'dest' may be changed during the * input process, however, since a '\0' is added at the end. There is no * newline at the end of 'dest'. * ---------------------------------------------------------------------------- */ char * TxGetLine(dest, maxChars) char *dest; int maxChars; { int i; char *ret; int ch; bool literal; if (maxChars < 1) return dest; if (txHavePrompt) TxUnPrompt(); ret = dest; dest[0] = '\0'; txReprint2 = dest; #define ERASE() ( (i>0) ?(i--, fputs("\b \b", stdout), 0) :0) (void) fflush(stderr); literal = FALSE; i = 0; while (TRUE) { dest[i] = '\0'; if (i >= maxChars - 1) break; (void) fflush(stdout); ch = TxGetChar(); if (ch == EOF || ch == -1 || ch == TxEOFChar) { TxError("\nEOF encountered on input stream.\n"); ret = NULL; break; } else if (literal) { literal = FALSE; dest[i] = ch; txFprintfBasic(stdout, "%c", ch); i++; } else if (ch == '\n' || ch == txBreakChar) { break; } else if (ch == '\t') { while (TRUE) { dest[i] = ' '; txFprintfBasic(stdout, " "); i++; if (((i + strlen(txReprint1)) % 8) == 0) break; } } else if (ch == txEraseChar) { ERASE(); } else if (ch == txKillChar) { while (i > 0) ERASE(); } else if (ch == txWordChar) { while (i > 0 && isspace(dest[i-1])) ERASE(); while (i > 0 && !isspace(dest[i-1])) ERASE(); } else if (ch == txReprintChar) { TxReprint(); } else if (ch == txLitChar) { literal = TRUE; } else { dest[i] = ch; txFprintfBasic(stdout, "%c", ch); i++; } } txFprintfBasic(stdout, "\n"); txHavePrompt = FALSE; txReprint2 = NULL; return ret; } /* * ---------------------------------------------------------------------------- * txGetTermState: * * Read the state of the terminal and driver. * * Results: * none. * * Side effects: * The passed structure is filled in. * ---------------------------------------------------------------------------- */ #ifdef SYSV void txGetTermState(buf) struct termio *buf; { ioctl( fileno( stdin ), TCGETA, buf); } #else void txGetTermState(buf) txTermState *buf; { ASSERT(TxStdinIsatty, "txGetTermState"); /* save the current terminal characteristics */ (void) ioctl(fileno(stdin), TIOCGETP, (char *) &(buf->tx_i_sgtty) ); (void) ioctl(fileno(stdin), TIOCGETC, (char *) &(buf->tx_i_tchars) ); } #endif /* * ---------------------------------------------------------------------------- * txSetTermState: * * Set the state of the terminal and driver. * * Results: * none. * * Side effects: * The terminal driver is set up. * ---------------------------------------------------------------------------- */ void txSetTermState(buf) #ifdef SYSV struct termio *buf; #else txTermState *buf; #endif { #ifdef SYSV ioctl( fileno(stdin), TCSETAF, buf ); #else /* set the current terminal characteristics */ (void) ioctl(fileno(stdin), TIOCSETN, (char *) &(buf->tx_i_sgtty) ); (void) ioctl(fileno(stdin), TIOCSETC, (char *) &(buf->tx_i_tchars) ); #endif } /* * ---------------------------------------------------------------------------- * txInitTermRec: * * Sets the terminal record that it has echo turned off, * cbreak on, and no EOFs the way we like it. * * Results: * The passed terminal record is modified. * * Side effects: * No terminal modes are actually changed. * ---------------------------------------------------------------------------- */ void txInitTermRec(buf) #ifdef SYSV struct termio *buf; #else txTermState *buf; #endif { #ifdef SYSV buf->c_lflag = ISIG; /* raw: no echo and no processing, allow signals */ buf->c_cc[ VMIN ] = 1; buf->c_cc[ VTIME ] = 0; #else /* set things up for us, turn off echo, turn on cbreak, no EOF */ buf->tx_i_sgtty.sg_flags |= CBREAK; buf->tx_i_sgtty.sg_flags &= ~ECHO; buf->tx_i_tchars.t_eofc = -1; /* Give the graphics module a chance to override this. */ if (GrTermStatePtr) (*GrTermStatePtr)(&buf->tx_i_sgtty, &buf->tx_i_tchars); #endif } #ifdef SYSV struct termio closeTermState; #else static txTermState closeTermState; #endif static bool haveCloseState = FALSE; /* * ---------------------------------------------------------------------------- * txSaveTerm: * * Save the terminal characteristics so they can be restored when * magic leaves. * * * Results: * none. * * Side effects: * a global variable is set. * ---------------------------------------------------------------------------- */ void txSaveTerm() { #ifdef SYSV ioctl( fileno( stdin ), TCGETA, &closeTermState); txEraseChar = closeTermState.c_cc[VERASE]; txKillChar = closeTermState.c_cc[VKILL]; TxEOFChar = closeTermState.c_cc[VEOF]; TxInterruptChar = closeTermState.c_cc[VINTR]; haveCloseState = TRUE; #else struct ltchars lt; txGetTermState(&closeTermState); (void) ioctl(fileno(stdin), TIOCGLTC, (char *) <); txEraseChar = closeTermState.tx_i_sgtty.sg_erase; txKillChar = closeTermState.tx_i_sgtty.sg_kill; txWordChar = lt.t_werasc; txReprintChar = lt.t_rprntc; txLitChar = lt.t_lnextc; txBreakChar = closeTermState.tx_i_tchars.t_brkc; TxEOFChar = closeTermState.tx_i_tchars.t_eofc; TxInterruptChar = closeTermState.tx_i_tchars.t_intrc; haveCloseState = TRUE; #endif } /* * ---------------------------------------------------------------------------- * TxSetTerminal: * * Sets the terminal up the way we like it. * * Results: * none. * * Side effects: * The terminal modes are changed. * ---------------------------------------------------------------------------- */ void TxSetTerminal() { #ifdef SYSV struct termio buf; #else txTermState buf; #endif if (TxStdinIsatty) { if (!haveCloseState) txSaveTerm(); buf = closeTermState; txInitTermRec(&buf); txSetTermState(&buf); } } /* * ---------------------------------------------------------------------------- * TxResetTerminal: * * Returns the terminal to the way it was when Magic started up. * * Results: * none. * * Side effects: * The terminal modes are changed. * ---------------------------------------------------------------------------- */ void TxResetTerminal() { if (TxStdinIsatty && haveCloseState) txSetTermState(&closeTermState); }