/*+++* * title: xon-xoff * abstract: check if terminal needs Xon-Xoff synchronization. * author: T.R.Hageman, Groningen, The Netherlands. * created: June 1992 * modified: (see Log at end of file) * description: * Usage: xon-xoff [-f] [-s] [pad time in ms (D=500)] [pad char (D=NUL)] * options: * -f force check (irrespective of value of XONXOFF). * -s output shell command to set XONXOFF environment variable. * * This program checks if the terminal can handle output at the current * tty line speed, without having to rely on Xon-Xoff synchronization. * It does so by writing a bunch of pad characters to stderr, then * waiting to see if the terminal generates any ^Q or ^S characters. * Depending on your system, this check can take from half a second to * up to 2 seconds. * * If the environment variable XONXOFF exists, and the -f option is * not given, the physical check is bypassed and the value of this * variable is used to determine the result: * XONXOFF=0 - if no Xon-Xoff synchronisation is needed, * XONXOFF=1 - if Xon-Xoff synchronisation is mandatory, * * If the -s option is given, a shell command to set this environment * variable is generated. Thus you can use the command: * * eval `xon-xoff -s` * * in your shell startup file to initialize XONXOFF. * (JOVEs system .joverc checks this variable to set "allow-^Q-and-^S"). * * exit value: * EXIT_SUCCESS - if no Xon-Xoff synchronisation is needed, * EXIT_FAILURE - if Xon-Xoff synchronisation is mandatory, * 2 - usage error. * bugs: * NOT YET TESTED on VMS. *---*/ #define NOT_JOVE #include "tune.h" #include RCS("$Id: xon-xoff.c,v 14.31 1993/02/15 02:01:52 tom Exp tom $") #ifndef _SYS_TYPES_H # if _I_SYS_TYPES - -1 # include # else # if _I_TYPES # include # endif # endif # define _SYS_TYPES_H #endif #include "tty.h" /* for TERMIOS/SGTTY */ #include "process.h" /* for ALARM_1_SEC, and */ #if _I_FCNTL # include #endif #if !_ANSI_HEADERS_ # ifndef errno extern int errno; # endif #endif #define OFF 0 #define ON 1 extern char *getenv __(( const char * )); int CharsPerSec; #if vms # include # include # include # include # include # include union u { long l; unsigned long ul; short w[2]; unsigned short uw[2]; char b[4]; unsigned char ub[4]; }; struct iosb { /* I/O status block */ union u u[2]; # define i_status u[0].uw[0] /* condition code */ # define i_count u[0].uw[1] /* transfer count */ # define i_ispeed u[0].ub[2] /* input speed *. # define i_ospeed u[0].ub[3] /* output speed */ # define i_terminator u[1].uw[0] # define i_termsize u[1].uw[1] }; struct termchar { /* Terminal characteristics */ union u u[3]; # define t_class u[0].ub[0] # define t_type u[0].ub[1] # define t_typeahdcnt u[0].uw[0] /* SENSEMODE|TYPEAHDCNT */ # define t_width u[0].uw[1] /* width in characters */ # define t_mode u[1].ul /* flags (+ page size) */ # define t_page u[1].ub[3] /* page size in lines */ # define t_xmode u[2].ul /* more flags */ }; #define SGTTYB struct termchar short _ttchan; #endif /* vms */ SGTTYB sg[2]; /* The following is almost literally snarfed from JOVEs tty.c */ #if (O_NDELAY && F_SETFL) # define LOOKAHEAD #endif /* * check if input is available on the keyboard. * unfortunately this is very dependent on your UNIX flavour. */ #ifdef LOOKAHEAD static int lookahead_char = -1; #endif int kbhit __(( void )); int kbhit() { #if unix #ifdef INWAIT /* some berkeley systems (??) */ { struct sg_brl gttyBuf; gtty(2, (char *) >tyBuf); return (gttyBuf.sg_xflags & INWAIT) != 0; } #endif /* INWAIT */ #if (O_NDELAY && F_SETFL) /* system V, XENIX, HP-UX... */ { if (lookahead_char < 0) { char c; fcntl(2, F_SETFL, O_NDELAY); /* turn blocking off */ if (read(2, &c, 1) > 0) /* anything available? */ lookahead_char = _UC_(c); fcntl(2, F_SETFL, !O_NDELAY); /* turn blocking on */ if (lookahead_char < 0) return 0; } return 1; } #endif (O_NDELAY && F_SETFL) #ifdef FIONREAD /* BSD (??) */ { long c; return (ioctl(2, FIONREAD, (struct sgttyb *) &c) == 0 && c > 0); } #endif /* FIONREAD */ #ifdef TIOCNRD /* (some versions of??) 7th Ed. */ { int c; return (ioctl(2, TIOCNRD, (struct sgttyb *) &c) == 0 && c > 0); } #endif /* TIOCNRD */ #ifdef c70 /* (??) */ return !empty(2); #endif #endif /* unix */ #if vms struct iosb iosb; struct termchar tc; return ((sys$qiow(0, _ttchan, IO$_SENSEMODE|IO$M_TYPEAHDCNT, &iosb, 0,0, &tc, sizeof tc, 0,0,0,0) & STS$M_SUCCESS) && (iosb.i_status & STS$M_SUCCESS) && (tc.t_count > 0)); #endif /* vms */ return -1; /* if none of the above compiles... */ } /* kbhit */ /* get a single character from stderr. */ int getch __(( void )); int getch() { char c; #ifdef LOOKAHEAD int ch; if ((ch = lookahead_char) >= 0) { lookahead_char = -1; return ch; } #endif if (read(2, &c, 1) <= 0) return -1; return _UC_(c); } /* getch */ /* determine terminal speed, in characters per second */ #define CPS(baud) (((baud) + 5) / 10) #if (B9600 == 96 && B2400 == 24) /* Minix (1.5) encoding (baud/100). */ # define SPEED(ospeed) ((ospeed) ? CPS(ospeed * 100) : CSPEED) #endif #ifndef SPEED static const int speeds[] = { CSPEED, CPS(50), CPS(75), CPS(110), CPS(134), CPS(150), #if (B200 && !vms) CPS(200), #endif CPS(300), CPS(600), #if (B900) CPS(900), #endif CPS(1200), CPS(1800), #if (B2000 || vms) CPS(2000), #endif CPS(2400), #if (B3600 || vms) CPS(3600), #endif CPS(4800), #if (B7200 || vms) CPS(7200), #endif CPS(9600), CPS(19200), CPS(38400) }; #define _SPEED(i) \ ((unsigned)(i) < sizeof(speeds)/sizeof(speeds[0]) ? speeds[i] : CSPEED) #ifndef B50 # define SPEED(ospeed) _SPEED(ospeed) #else # define SPEED(ospeed) _SPEED((ospeed) - (B50 - 1)) #endif #endif /* SPEED */ /* Set tty in raw-ish mode. */ void ttinit __(( void )); void ttinit() { #if unix #ifdef TERMIOS tcgetattr(2, &sg[OFF]); sg[ON] = sg[OFF]; sg[ON].c_iflag &= ~(IXON|IXOFF); sg[ON].c_lflag &= ~(ICANON|ECHO); sg[ON].c_cc[VMIN] = 1; sg[ON].c_cc[VTIME] = 0; tcsetattr(2, TCSANOW, &sg[ON]); CharsPerSec = SPEED(cfgetospeed(&sg[ON])); #else gtty(2, &sg[OFF]); sg[ON] = sg[OFF]; sg[ON].sg_flags |= RAW; stty(2, &sg[ON]); CharsPerSec = SPEED(sg[ON].sg_ospeed); #endif #endif /* unix */ #if vms $DESCRIPTOR(sys_input, "SYS$INPUT:"); int st; struct iosb iosb; /* assign a channel for terminal i/o. */ if (!((st = sys$assign(&sys_input, &_ttchan)) & STS$M_SUCCESS) || /* get terminal characteristics */ !((st = sys$qiow(0, /* wait on event flag 0 */ _ttchan, /* channel to terminal */ IO$_SENSEMODE, /* command code */ &iosb, /* status of operation */ 0,0, /* no AST */ &sg[OFF], sizeof sg[OFF]) /* result buffer */ ) & STS$M_SUCCESS) || !((st = iosb.i_status) & STS$M_SUCCESS)) exit(st); # if 0 if (sg[OFF].t_class != DC$_TERM) exit(``SS$_NOTATERM''); # endif sg[ON] = sg[OFF]; sg[ON].t_mode |= TT$M_NOECHO; sg[ON].t_mode &= ~(TT$M_TTSYNC); sg[ON].t_xmode |= TT2$M_PASTHRU|TT2$M_ALTYPEAHD; sys$qiow(0, _ttchan, IO$_SETMODE, 0,0,0, &sg[ON], sizeof sg[ON], 0,0,0,0); CharsPerSec = SPEED(iosb.i_ospeed); #endif /* vms */ } /* Restore original tty modes. */ void ttreset __(( void )); void ttreset() { #if unix #ifdef TERMIOS tcsetattr(2, TCSANOW, &sg[OFF]); #else stty(2, &sg[OFF]); #endif #endif /* unix */ #if vms sys$qiow(0, _ttchan, IO$_SETMODE, 0,0,0, &sg[OFF], sizeof sg[OFF], 0,0,0,0); #endif /* vms */ } void putstr __(( int _(fd), const char *_(str) )); void putstr(fd, str) int fd; const char *str; { write(fd, str, strlen(str)); } void usage __(( void )); void usage() { putstr(2, "\ usage: xon-xoff [-f] [-s] [pad time (in ms; D=500)] [pad char (D=NUL)]\n\ options:\n\ -f force check (irrespective of value of XONXOFF).\n\ -s output shell command to set XONXOFF environment variable.\n\ "); exit(2); } /* Output commands to set XONXOFF environment variable. */ void setenv __(( int _(how) )); void setenv(how) { #if !vms static char template[] = "setenv XONXOFF=0\nexport XONXOFF\n"; /*01234567890123456 7*/ register char *command = template; register const char *sh = getenv("SHELL"); if (how != EXIT_SUCCESS) ++command[15]; /* 0 => 1 */ if (sh == NULL || strcmp(&sh[strlen(sh)-3], "csh") != 0) /* Assume Bourne shell. */ command += 7; /* skip "setenv " */ else /* Assume C-shell or one of its clones. */ command[14] = ' ', /* replace '=' with space */ command[17] = '\0'; /* chop off "export..." */ #else static char command[] = "XONXOFF:==0\n"; if (how != EXIT_SUCCESS) ++command[10]; /* 0 => 1 */ #endif /* vms */ putstr(1, command); } /* Dummy SIGALRM handler for time-out. */ sig_tp shoot __(( int _(sig) )); sig_tp shoot(sig) { } char null_buf[3840]; int main(argc, argv) int argc; char *argv[]; { register char *s; register unsigned ms = 500; register char c = '\0'; int force = 0; int set = 0; int result = EXIT_SUCCESS; while (argc > 1 && argv[1][0] == '-') { switch (argv[1][1]) { case 'f': force++; break; case 's': set++; break; default: usage(); } argv++, argc--; } if (!force) if (s = getenv("XONXOFF")) return (*s == '0') ? EXIT_SUCCESS : EXIT_FAILURE; if (argc > 1) { ms = atoi(argv[1]); if (ms == 0) ms = 500; if (ms > 1000) ms = 1000; if (argc > 2) { c = argv[2][0]; if (c == '^') { switch (c = argv[2][1]) { case '\0': c = '^'; break; case '?': c = '\177'; break; default: c &= '\037'; break; } } for (s = &null_buf[(long)CharsPerSec * ms / 1000]; s > null_buf; ) *--s = c; } } if (!isatty(2)) { putstr(2, "xon-xoff: stderr should be a tty!\n"); exit(2); } ttinit(); write(2, null_buf, (int)((long) CharsPerSec * ms / 1000)); /* set timeout in case kbhit() doesn't work. */ signal(SIGALRM, shoot); alarm(ALARM_1_SEC); while (kbhit()) { switch(getch()) { case EOF: break; case '\021': /* Ctrl-Q */ case '\023': /* Ctrl-S */ result = EXIT_FAILURE; default: continue; } break; } alarm(0); ttreset(); if (set) setenv(result); return result; } /*====================================================================== * $Log: xon-xoff.c,v $ * Revision 14.31 1993/02/15 02:01:52 tom * remove (void) casts. * * Revision 14.30 1993/01/26 18:43:18 tom * cleanup whitespace. * * Revision 14.28 1992/10/23 17:15:59 tom * convert to "port{ansi,defs}.h" conventions. * * Revision 14.26 1992/08/26 23:57:07 tom * add RCS directives. * * Revision 14.25 1992/07/25 01:15:15 tom * improve recognition of csh clones. */