/* @(#)ost.c 17.1.1.1 (ES0-DMD) 01/25/02 17:35:09 */ /*=========================================================================== Copyright (C) 1995 European Southern Observatory (ESO) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, MA 02139, USA. Corresponding concerning ESO-MIDAS should be addressed as follows: Internet e-mail: midas@eso.org Postal address: European Southern Observatory Data Management Division Karl-Schwarzschild-Strasse 2 D 85748 Garching bei Muenchen GERMANY ===========================================================================*/ /*++++++++++++++++++++ .TYPE Module .IDENTIFICATION ost .AUTHOR Guido Russo [ST-ECF], Francois Ochsenbein [ESO-IPG] .LANGUAGE C .KEYWORDS Terminal i/o .ENVIRONMENT POSIX complaint. .COMMENTS Adapted from Toolpack TIE WINDOWS. There is a switch here, named TIMEOUT, for BSD version, which inhibates the timeout in ostread. .VERSION 1.0 30-Jan-1986: Creation .VERSION 1.6 16-Jun-1987: Adapted for Ibm-PC, G. Russo, M. Albrecht. .VERSION 1.7 22-Jun-1987: Adapted for UNIX .VERSION 1.8 19-Jan-1988: Transformed to os MIDAS routines. .VERSION 2.0 14-Oct-1988: ^Z disabled on Sun - Added ostraw .VERSION 2.1 24-Jul-1989: ostint returns previously defined interrupt function .VERSION 2.1 29-May-1990: trap some errors. .VERSION 2.2 05-Jul-1990: ostint works also if terminal not opened .VERSION 2.3 28-Aug-1990: Use file descriptor 2 if 0 or 1 is not a tty. Added option 2 for ostraw (only input raw) .VERSION 2.6 06-Nov-1990: Added ostwinch (Window Change) .VERSION 4.0 05-Apr-1993: POSIX compliant. ----------------------------------------------------------------------------*/ /* * signal.h produces compilation error if POSIX_SOURCE is defined on Solaris * 2.3 (I believe a bug). If this happens, remove the POSIX_SOURCE definition * or move the "#include signal.h" before the POSIX_SOURCE. */ /* * Define _POSIX_SOURCE to indicate * that this is a POSIX program */ #define _POSIX_SOURCE 1 #include /* ANSI-C prototyping */ #include #include #include #include #include #include #include #include /* Terminal Definitions */ #ifdef OSERROR_D /* oserror already defined (Silicon G)*/ #define oserror midaserror #endif extern char *oserrmsg; extern int oserror; void winsize(); char *getenv(); /* int ospexit(); */ int oscopy(/*dest, source, len*/); typedef void (*FUNCTION)(); /* Pointer to Function */ #define MIN(x,y) ((x) <= (y) ? (x):(y)) /* Minimum */ #define WAITING 1 /* 1s of Timeout */ #define AHEAD 127 /* Size of type-ahead buffer */ /* sigjmp_buf is not defined in Linux SLS 1.03 0.99pl9 * but it is defined in Linux SLACKWARE 1.2, 0.99pl12 * We can not use jmp_buf, because in Linux it does not save the sig_mask */ static sigjmp_buf env; static char buf_name[24]; static struct termios tty0; static struct termios tty1; static char ahead_buffer[AHEAD] = ""; static int nahead = 0; /* Number of char. in ahead_buffer */ static char opened_term = 0; static int mytty = STDERR_FILENO; /* File Descriptor used as terminal */ static int myout = STDERR_FILENO; /* File Descriptor used as output */ #ifdef __STDC__ /* ARGSUSED */ static void ostalarm(int sig) #else static void ostalarm(sig) /*++++++++++++++++ .PURPOSE Function called at end of time-out .RETURNS Within ostread function. Internal use only. ---------*/ int sig; #endif { siglongjmp(env,1); /* return with the saved signal mask from sigsetjmp */ } /*static void oststop(s) */ void oststop(s) /*++++++++++++ .PURPOSE Terminate program in case of interrput .RETURNS To OS ----------------------------------------------------------------------------*/ int s; /* IN: Signal identification */ { static char msg[] = "\r\n**** Killed by signal "; static char asig[]= " (XX)\r\n\n"; char *p; oserror = 0; (void) ostclose(); (void) write(myout, msg, sizeof(msg)-1); switch(s) { #ifdef SIGBUS case SIGBUS: p = "BUS"; break; #endif case SIGPIPE: p = "PIPE"; break; case SIGTSTP: p = "TSTP"; break; case SIGTERM: p = "TERM"; break; case SIGHUP: p = "HUP"; break; case SIGALRM: p = "ALARM"; break; default: p = ""; break; } (void) write(myout, p, strlen(p)); asig[2] = '0' + s/10; asig[3] = '0' + s%10; (void) write(myout, asig, sizeof(asig)-1); ospexit(oserror); } static FUNCTION f_int = oststop; /* Interrupt function */ #ifdef __STDC__ static int inhibit(void) #else static int inhibit() /*+++++++++++ .PURPOSE Inhibit the Quit and SUSP keyword .RETURNS 0 (sucess) / -1 (failure) -------------*/ #endif { long temp; /* * 930630: On PC_SCO _POSIX_VDISABLE is defined as (unsigned char)-1 * which can not be used in the next condition inclusion. */ #if defined(PC_SCO) # undef _POSIX_VDISABLE # define _POSIX_VDISABLE -1 #endif #if defined(_POSIX_VDISABLE) && (_POSIX_VDISABLE != -1) /* The symbol is defined so we can just use it */ tty1.c_cc[VQUIT] = _POSIX_VDISABLE; tty1.c_cc[VSUSP] = _POSIX_VDISABLE; #else /* The symbol is not defined */ errno = 0; /* Make sure we can tell if fpathconf() changes errno. */ /* * See if it is defined for the terminal */ temp = fpathconf(mytty,_PC_VDISABLE); if (temp != -1) { /* temp != -1. This is the value to use */ tty1.c_cc[VQUIT] = (cc_t)temp; tty1.c_cc[VSUSP] = (cc_t)temp; return(0); } /* * We get here if fpathconf() returned -1. If * errno is changed then ther was a real error */ if (errno != 0) return(-1); /* We get here if we cannot disable the suspend character. * Fall back on the unlikely character. */ tty1.c_cc[VQUIT] = 0377; tty1.c_cc[VSUSP] = 0377; #endif return(0); } int ostopen () /*+++++++++++ .PURPOSE Initialize the terminal. .RETURNS 0 (sucess) / -1 (failure), oserror explains .REMARKS If no terminal attached, returns -1. -------------*/ { struct sigaction act, oact; oserror = 0; /* * Get the terminal status */ if (!isatty(STDERR_FILENO)) { mytty = STDIN_FILENO, myout = STDOUT_FILENO; if (isatty(mytty) && isatty(myout)) ; else { oserror = -1, oserrmsg = "No attached terminal"; return(-1); } } if (tcgetattr(mytty,&tty0) != 0) { oserror = errno; return(-1); } (void) tcgetattr(mytty,&tty1); /* * Change Echo and Raw modes */ tty1.c_lflag &= ~(ICANON|ECHO); tty1.c_iflag &= ~(ICRNL); /* Don't change CR to NL */ tty1.c_cc[VMIN] = 0; /* Length of type-ahead buffer */ tty1.c_cc[VTIME] = WAITING*10;/* Waiting time in .1 sec */ /* * Inhibate Quit and SUSP characters */ if (inhibit() != 0) { oserror = errno; return(-1); } #if DEBUG printf("Operation on files: input=%d / output=%d\n", mytty, myout); printf("Initial parameters: oflag=%-7o VMIN=%3d VTIME=%3d\n", tty0.c_oflag, tty0.c_cc[VMIN], tty0.c_cc[VTIME]); printf(" .. will be set to: %-7o %3d %3d\n", tty1.c_oflag, tty1.c_cc[VMIN], tty1.c_cc[VTIME]); ospwait(20); #endif act.sa_handler = oststop; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (sigaction(SIGPIPE,&act,&oact) != 0) { oserror = errno; return(-1); } if (sigaction(SIGTSTP,&act,&oact) != 0) { oserror = errno; return(-1); } if (sigaction(SIGHUP,&act,&oact) != 0) { oserror = errno; return(-1); } if (sigaction(SIGTERM,&act,&oact) != 0) { oserror = errno; return(-1); } if (sigaction(SIGQUIT,&act,&oact) != 0) { oserror = errno; return(-1); } #ifdef SIGBUS /* No-POSIX signal */ if (sigaction(SIGBUS,&act,&oact) != 0) { oserror = errno; return(-1); } #endif /* * Define the trap routine in case of Interrupt */ act.sa_handler = f_int; if (sigaction(SIGINT,&act,&oact) != 0) { oserror = errno; return(-1); } /* * By default ignore ALRM signals * Otherwise it could be killed by intermail() - /monit/prepa.c */ act.sa_handler = SIG_IGN; if (sigaction(SIGALRM,&act,&oact) != 0) { oserror = errno; return(-1); } if (tcsetattr(mytty,TCSAFLUSH, &tty1)) { oserror = errno; return(-1); } opened_term = 1; #if DEBUG (void) write(myout,"ostopen executed\r\n", 18); #endif return(0); } int ostclose() /*+++++++ .PURPOSE Close the terminal (reset it to normal) .RETURNS 0 / -1 on failure --------------*/ { oserror = 0; #if DEBUG printf("\nostclose: were : VMIN=%d,VTIME=%d. =>(init) %d, %d\n\r", tty1.c_cc[VMIN], tty1.c_cc[VTIME], tty0.c_cc[VMIN], tty0.c_cc[VTIME]); #endif if (opened_term) /* Reset Terminal */ if (tcsetattr(mytty,TCSAFLUSH, &tty0)) { oserror = errno; return(-1); } opened_term = 0; return(0); } int ostset(tstat) /*++++ .PURPOSE Set Terminal. .RETURNS 0 / -1 .REMARKS Only interrupt / quit controls are reset. ---------------------------------*/ struct termstatus *tstat; /* IN: Status structure with controls to modify */ { oserror = 0; if (isatty(mytty) && isatty(myout)) { if (tcgetattr(mytty,&tty1) != 0) { oserror = errno; return(-1); } tty1.c_cc[VINTR] = tstat->cc_INT; tty1.c_cc[VQUIT] = tstat->cc_QUIT; if (tcsetattr(mytty,TCSANOW, &tty1)) { oserror = errno; return(-1); } } else { oserror = -1; oserrmsg = "Can't change terminal setup"; return(-1); } return(0); } int ostraw(op) /*++++ .PURPOSE Set Terminal to raw (op = 1), to Normal (0), to OutputRaw (2) .RETURNS 0 / -1 .REMARKS The 2 mode (OutputRaw) sets VTIME to 0 and VMIN to 1 (untimeout). ---------------------------------*/ int op; /* IN: 0 to reset to standard, 1 to reset to raw */ { oserror = 0; if (tcgetattr(mytty,&tty1) != 0) { oserror = errno; return(-1); } #if DEBUG printf("ostraw: op=%d. Initial: VMIN=%d,VTIME=%d. Current %d,%d.",op, tty0.c_cc[VMIN], tty0.c_cc[VTIME], tty1.c_cc[VMIN], tty1.c_cc[VTIME]); #endif switch(op) { default: /* ==> to RAW mode */ tty1.c_lflag &= ~(ICANON|ECHO); tty1.c_iflag &= ~ICRNL; /* Don't change CR to NL */ tty1.c_cc[VMIN] = 0; /* Length of type-ahead buffer */ tty1.c_cc[VTIME] = WAITING*10; /* Waiting time in .1 sec */ break; case 0: /* ==> to NORMAL mode */ tty1.c_cc[VMIN] = tty0.c_cc[VMIN] ; tty1.c_cc[VTIME] = tty0.c_cc[VTIME]; tty1.c_lflag = tty0.c_lflag; tty1.c_iflag = tty0.c_iflag; break; case 2: /* ==> Untimeout input */ tty1.c_lflag &= ~(ICANON|ECHO); tty1.c_iflag &= ~ICRNL; /* Don't change CR to NL */ tty1.c_cc[VMIN] = 1; /* Length of type-ahead buffer */ tty1.c_cc[VTIME] = 0; /* No timer */ break; } # if DEBUG printf("Now %d,%d\n", tty1.c_cc[VMIN], tty1.c_cc[VTIME]); # endif if (tcsetattr(mytty,TCSANOW, &tty1)) { oserror = errno; return(-1); } return(0); } FUNCTION ostint(f) /*++++ .PURPOSE Define a function for interrupt .RETURNS Previous function ----------------------------------------------------------------------------*/ FUNCTION f; /* IN: Function to call in case of Interrupt */ { struct sigaction act, oact; oserror = 0; act.sa_handler = f; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (sigaction(SIGINT,&act,&oact) != 0) { oserror = errno; return(SIG_ERR); }; f_int = f; return(oact.sa_handler); } FUNCTION ostwinch(f) /*++++ .PURPOSE Define a function for Window Change .RETURNS Previous function ----------------------------------------------------------------------------*/ FUNCTION f; /* IN: Function to call in case of Window Change */ { struct sigaction act, oact; oserror = 0; #ifdef SIGWINCH act.sa_handler = f; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (sigaction(SIGWINCH,&act,&oact) != 0) { oserror = errno; return(SIG_ERR); } return(oact.sa_handler); #else # ifdef ENOSYS oserror = ENOSYS; # else oserror = -1, oserrmsg = "WINCH not available"; # endif return(SIG_ERR); #endif } int ostin() /*+++ .PURPOSE Retrieve the number of characters waiting for input (type-ahead buffer) .RETURNS Number of bytes waiting in type-ahead buffer / -1 for error. .REMARKS Only possible when terminal is in RAW mode. --------*/ { int icanon=0; # if DEBUG printf("Now %d,%d\n", tty1.c_cc[VMIN], tty1.c_cc[VTIME]); # endif oserror = 0; if (nahead < 0) nahead = 0; if (nahead == 0) { /* If terminal in ICANON mode then set to RAW */ if (tty1.c_lflag & ICANON) { icanon=1; ostraw(1); } /* with VMIN & VTIME to 0, read() returns as much data without waiting */ tty1.c_cc[VTIME] = 0; if (tcsetattr(mytty,TCSANOW, &tty1)) { oserror = errno; return(-1); } if ((nahead = read(mytty, ahead_buffer, AHEAD)) < 0) oserror = errno; /* Reset to previous values */ if (tcsetattr(mytty,TCSANOW, &tty1)) { oserror = errno; return(-1); } if (icanon) ostraw(0); /* set back to ICANON mode */ } return(nahead); } int ostread(string, length, timeout) /*+++ .PURPOSE Input from the terminal (a timeout is REQUIRED!). A null timeout performs the reading of what is in the type-ahead buffer only. \begin{TeX} This routine must perform binary input --- \ie the input is not echoed or edited, and the program receives the user's input without having to wait for a RETURN / newline / etc. If the user has not typed anything within the maximum time passed (in seconds and max. 25) it should return with the value zero. It is desirable (but not strictly necessary) that the "wait" be cut short if input is received after the routine call but before the expiry of the time period --- if this is not the case then the calling routines may simply be a bit slow. \end{TeX} .RETURNS Number of bytes read / -1 (End of File or Failure) -----------------------------------------------------------------------*/ char *string; /* OUT: string read */ int length; /* IN: size of string */ int timeout; /* IN: Timeout time in seconds, max 25 */ { register int l; unsigned int time; struct sigaction act, oact; oserror = 0; if (length <=0) /* Impossible to get 0 char */ { oserror = EINVAL; return(-1); } /* Test timeout. Max 25 sec */ time = (timeout < 0 ? 0 : timeout); time = (time > 25 ? 25 : time); if (nahead > 0) { l = MIN(nahead, length); oscopy(string, ahead_buffer, l); nahead -= l; /* Remaining bytes */ if (nahead) oscopy(&ahead_buffer[0], &ahead_buffer[l], nahead); return(l); } tty1.c_cc[VTIME] = (unsigned char)time*10; /* Timeout in seconds */ if (tcsetattr(mytty,TCSANOW, &tty1)) { oserror = errno; return(-1); } act.sa_handler = ostalarm; sigemptyset(&act.sa_mask); act.sa_flags = 0; (void) sigaction(SIGALRM,&act,&oact); if ( sigsetjmp(env,1) == 0 ) { for (;;) { if ( (l = read(mytty, string, length)) < 0) { if (errno == EINTR) continue; else oserror = errno; } break; } } else l = 0; /* Was interrupted */ (void) sigaction(SIGALRM,&oact,&act); /* set SIGALRM to previous value */ tty1.c_cc[VTIME] = WAITING*10; /* Reset 1s time-out */ if (tcsetattr(mytty,TCSANOW, &tty1)) { oserror = errno; return(-1); } return(l); } int ostwrite(string, length) /*+++ .PURPOSE Physical output of a string to the terminal. .RETURNS Number of bytes written / -1 (failure, oserror explains) -----------------------------------------------------------------------------*/ char *string; /* IN: string to be written */ int length; /* IN: length of string */ { register int l; oserror = 0; if (l = write(myout, string, length) < 0) { oserror = errno; return(-1); } return(l); } int ostinfo(tstat) /*+++++ .PURPOSE Get info about the terminal in termstatus structure (see osterm.h), i.e. terminal name, number of lines, of columns, baud rate, and interrupt control characters. .RETURNS 0 (sucess) / -1 (failure), oserror explains .REMARKS If no terminal attached, returns -1. --------------*/ struct termstatus *tstat; /* OUT: Terminal description */ { register speed_t baud; register int i; register char *p, *q; unsigned short col, row; oserror = 0; /* * Get the terminal status */ if (!isatty(STDERR_FILENO)) { mytty = STDIN_FILENO; myout = STDOUT_FILENO; if (isatty(mytty) && isatty(myout)) ; else { oserror = -1, oserrmsg = "No attached terminal"; return(-1); } } if (tcgetattr(mytty,&tty1) != 0) { oserror = errno; return(-1); } baud = cfgetispeed(&tty1); switch(baud) { case B150: case B200: case B134: i = 150; break; case B110: i = 110; break; case B50: i = 50 ; break; case B75: i = 75 ; break; case B300: i = 300; break; case B600: i = 600; break; case B1200: i = 1200; break; case B1800: i = 1800; break; default: case B2400: i = 2400; break; case B4800: i = 4800; break; case B9600: i = 9600; break; case B19200: i = 19200; break; case B38400: i = 38400; break; } tstat->baud_rate = i; tstat->cc_INT = tty1.c_cc[VINTR]; tstat->cc_QUIT = tty1.c_cc[VQUIT]; winsize(mytty,&col,&row); tstat->lines = row; tstat->columns = col; /* * Copy terminal name to buffer */ p = getenv("TERM"); /* Get Terminal Type */ if (!p) p = getenv("term"); if (!p) p = "unknown"; /* Default terminal name */ tstat->termname = p; p = tstat->termname; i = strlen(p); if (i > sizeof(buf_name)-1) i = sizeof(buf_name)-1; for (q = buf_name; --i >= 0; ) *(q++) = *(p++); *q = '\0'; /* End Of String */ tstat->termname = buf_name; return(0); }