char *ckxv = "OS-9 tty I/O, 5A(08) 12 Oct 92"; /* C K 9 T I O */ /* C-Kermit interrupt, terminal control & i/o functions for os9/68k systems */ /* Author: Peter Scholz Ruhr University Bochum, Department of analytical chemistry Federal Republic of Germany, February 1987 Bob Larson (Blarson@ecla.usc.edu) Cleanup, fix timeouts, fix connect escape Rewrite ttinl for less overhead Edition: 5A(01) Chris Hemsing (Chris@lfm.rwth-aachen.de) Cleaned up timed i/o using F$Alarm Tmode/Xmode stuff Send break stuff All time related stuff new 07/25/91 Chris Hemsing minor bug fixes, changes for gnu (ansi) C 12/02/91 Bob Larson Blarson@usc.edu alarm() code was a nice idea, but doesn't work. Fixed back to code that will interupt i/o in progress. Minor cleanup. Retrofited current unix myread() code to speed things back up. 01/14/92 Chris Hemsing minor xmode bug fixes, sigmask <->longjump OS-9 does interrupt i/o, but bad drivers don't -> alarm code not reintroduced. Edition: 5A(05) Bob Larson conchk bug fix Edition: 5A(06) 03/04/92 Chris Hemsing ttopen: dup(0) fix, compare upper case Edition: 5A(06) 09/28/92 Chris Hemsing cccbrk bug fix Edition: 5A(06) 10/12/92 Chris Hemsing improved CTRL-E handling (catch) adapted from unix C-Kermit Author: Frank da Cruz (SY.FDC@CU20B), Copyright (C) 1985, 1992, Trustees of Columbia University in the City of New York. Permission is granted to any individual or institution to use this software as long as it is not sold for profit. This copyright notice must be retained. This software may not be included in commercial products without written permission of Columbia University. */ #include /* Directory */ #ifdef NULL #undef NULL #endif NULL #ifndef GREGORIAN #define GREGORIAN 0 #define JULIAN 1 #endif #include /* Interrupts */ #include #include /* Set/Get tty modes */ #include /* setstat/getstat function codes */ #include #include #include #include #include /* osk i/o modes S_IREAD, etc */ #include "ckcdeb.h" /* Typedefs, formats for debug() */ /* Maximum length for the name of a tty device */ #ifndef DEVNAMLEN #define DEVNAMLEN MAXNAMLEN*4+2 /* leave room for / and terminating \0 */ #endif /* not really an OS-9 restriction */ /* *4 is arbitrary */ /* nfm getstat call bug: SS_DevNm always copies all 32 bytes, nfm does the first part of the name => sum is more than 32 bytes e.g "term": all fine because 32 bytes incl. "term" are copied "n0/node1/term": "n0/node1/" done by nfm "term" is done by remote's scf (32 bytes) */ char *ckxsys = " OS-9/68000"; #define MYREAD /* use improved input code */ /* Warning: untested without this defiend */ /* Variables available to outside world: dftty -- Pointer to default tty name string, like "/dev/tty". dfloc -- 0 if dftty is console, 1 if external line. dfprty -- Default parity dfflow -- Default flow control ckxech -- Flag for who echoes console typein: 1 - The program (system echo is turned off) 0 - The system (or front end, or terminal). functions that want to do their own echoing should check this flag before doing so. Functions for assigned communication line (either external or console tty): syscleanup() -- System dependent shutdown sysinit() -- System dependent program initialization ttopen(ttname,local,mdmtyp,timo) -- Open the named tty for exclusive access. ttclos() -- Close & reset the tty. ttpkt(speed,flow) -- Put the tty in packet mode and set the speed. ttvt(speed,flow) -- Put the tty in virtual terminal mode. or in DIALING or CONNECTED modem control state. ttinl(dest,max,timo) -- Timed read line from the tty. ttinc(timo) -- Timed read character from tty. myread() -- Raw mode bulk buffer read, gives subsequent chars one at a time and simulates FIONREAD. myunrd(c) -- Places c back in buffer to be read (one only) ttchk() -- See how many characters in tty input buffer. ttxin(n,buf) -- Read n characters from tty (untimed). ttol(string,length) -- Write a string to the tty. ttoc(c) -- Write a character to the tty. ttflui() -- Flush tty input buffer. */ /* Functions for console terminal: congm() -- Get console terminal modes. concb(esc) -- Put the console in single-character wakeup mode with no echo. conbin(esc) -- Put the console in binary (raw) mode. conres() -- Restore the console to mode obtained by congm(). conoc(c) -- Unbuffered output, one character to console. conol(s) -- Unbuffered output, null-terminated string to the console. conola(s) -- Unbuffered output, array of strings to the console. conxo(n,s) -- Unbuffered output, n characters to the console. conchk() -- Check if characters available at console (bsd 4.2). Check if escape char (^\) typed at console (System III/V). coninc(timo) -- Timed get a character from the console. conint() -- Enable terminal interrupts on the console if not background. connoi() -- Disable terminal interrupts on the console if not background. Time functions msleep(m) -- Millisecond sleep ztime(s) -- Return date/time string rtimer() -- Reset timer gtimer() -- Get elapsed time since last call to rtimer() */ /* Declarations */ extern int errno; /* System call error return */ /* dftty is the device name of the default device for file transfer */ /* dfloc is 0 if dftty is the user's console terminal, 1 if an external line */ char *dftty; /* Remote by default, use normal */ int dfloc = 0; /* controlling terminal name. */ int dfprty = 0; /* Parity (0 = none) */ int ttprty = 0; /* current parity */ static int ttpmsk = 0377; /* Parity stripping mask. */ int dfflow = 1; /* Xon/Xoff flow control */ int backgrd = 0; /* Assume in foreground (no '&' ) */ int ckxech = 0; /* 0 if system normally echoes console characters, else 1 */ int ttypn = -1; /* TTY path number, also used in ck9con.c */ int ttnproto = 0; /* Network protocol, 0 = none. */ int ttcarr = CAR_AUT; /* Carrier handling mode. */ char myttystr[DEVNAMLEN]; /* Declarations of variables global within this module */ static char ttnmsv[DEVNAMLEN]; /* always name of ttypn device */ static char dfttystr[DEVNAMLEN]; static int xlocal = 0; /* Flag for tty local or remote */ static struct sgbuf /* sgtty info... */ ttold, ttraw, tttvt, /* for communication line */ ccold, ccraw, cccbrk; /* and for console */ static time_t tcount; /* Elapsed time counter */ static time_t timowhen; /* timeout time */ static SIGTYP (*saval)() = NULL; /* For saving alarm handler */ static jmp_buf sjbuf; /* Longjump buffers */ static SIGTYP timerh(); #define B50 0 #define B75 1 #define B110 2 #define B134 3 #define B150 4 #define B300 5 #define B600 6 #define B1200 7 #define B1800 8 #define B2000 9 #define B2400 10 #define B3600 11 #define B4800 12 #define B7200 13 #define B9600 14 #define B19200 15 #define B38400 16 static unsigned short bdrts[] = { 50, 75, 110, 134, 150, 300, 600, 1200, 1800, 2000, 2400, 3600, 4800, 7200, 9600, 19200, 38400 }; /* for unix signal emulation */ #define MAXSIG 4 typedef VOID (*fptr)(); fptr sigtbl[MAXSIG+1]; fptr signal(); syscleanup() { return 0; /* none needed */ } /* S Y S I N I T -- System-dependent program initialization. */ sysinit() { /* initialize unix signal emulation */ register int i; register char *cp; VOID catch(); char *getenv(); for(i=0;i<=MAXSIG;i++) sigtbl[i] = SIG_DFL; intercept(catch); if(isatty(0)) { /* must disable buffering before io */ setbuf(stdin,(char *)NULL); setbuf(stdout,(char *)NULL); } /* get terminal name from enviornment variable PORT, * use device of stderr as a backup */ myttystr[0] = '/'; if((cp = getenv("PORT")) == NULL) { if(_gs_devn(2, myttystr+1) < 0) return -1; } else strcpy(myttystr, cp); strcpy(dfttystr, myttystr); dftty = &dfttystr[0]; backgrd = (isatty(0) && isatty(1)) ? 0 : 1; congm(); return 0; } /* T T O P E N -- Open a tty for exclusive access. */ /* Call with: ttname: character string - device name or network host name. lcl: If called with lcl < 0, sets value of lcl as follows: 0: the terminal named by ttname is the job's controlling terminal. 1: the terminal named by ttname is not the job's controlling terminal. But watch out: if a line is already open, or if requested line can't be opened, then lcl remains (and is returned as) -1. modem: Less than zero: ttname is a network host name. Zero or greater: ttname is a terminal device name. Zero means a local connection (don't use modem signals). Positive means use modem signals. timo: 0 = no timer. nonzero = number of seconds to wait for open() to return before timing out. Returns: 0 on success -5 if device is in use -4 if access to device is denied -3 if access to lock directory denied -2 upon timeout waiting for device to open -1 on other error */ int ttopen(ttname,lcl,modem,timo) char *ttname; int *lcl, modem, timo; { char dev_buffer[DEVNAMLEN]; debug(F111,"ttopen entry modem",ttname,modem); debug(F101," ttypn","",ttypn); debug(F101," lcl","",*lcl); if (ttypn > -1) /* if device already opened */ { if (ustrncmp(ttname,ttnmsv,DEVNAMLEN))/* are new & old names equal? */ /* compare ignoring case */ ttclos(0); /* no, close old ttname, open new */ else /* else same, ignore this call, */ return(0); /* and return. */ } if (timo < 0) timo = 0; /* Safety */ if (setjmp(sjbuf)) /* Timer went off? */ { debug(F100,"ttopen timout","",0); /* Get here on timeout. */ return(-2); /* and return error code. */ } else { if (timo) { /* Don't time out if timo == 0 */ /* This won't work, since alarm() will not interupt i/o. */ /* Unfortunatatly, there is no non-blocking open to replace */ /* it with. blarson@usc.edu */ /* Alarm will interrupt i/o when using a proper driver that */ /* checks for pending signals smaller than deadly */ /* chris@lfm.rwth-aachen.de */ saval = signal(SIGALRM,timerh); /* Enable timer interrupt */ alarm((unsigned int)timo); /* Set it. */ } strncpy(ttnmsv,ttname,DEVNAMLEN); /* keep ttnmsv corresp. to ttypn */ dev_buffer[0] = '/'; _gs_devn(0,&dev_buffer[1]); /* get name of stdin */ if (*ttname=='\0' || ustrncmp(ttname,dev_buffer,DEVNAMLEN)==0) ttypn = open(ttname,S_IREAD|S_IWRITE); /* Try open for read/write; we cannot open own stdin non-share*/ /* Don't simply dup(0) or dup(1); they might only be open for mere read (0) or mere write (1) */ else ttypn = open(ttname,S_IREAD|S_IWRITE|S_ISHARE); /* Try to open for read/write nonshare*/ if (timo) ttimoff(); /* Turn off timer */ if (ttypn < 0) /* If couldn't open, fail. */ { perror(ttname); if ((errno == E_DEVBSY) || (errno == E_SHARE )) return(-5); return(-1); } } debug(F111,"ttopen ok",ttname,*lcl); /* Caller wants us to figure out if line is controlling tty */ if (*lcl == -1) { if (isatty(0)) { /* if stdin not redirected */ dev_buffer[0] = '/'; _gs_devn(0,&dev_buffer[1]); /* ...with real name of stdin */ xlocal = (ustrncmp(ttname,dev_buffer,DEVNAMLEN) != 0); debug(F111," ttyname",dev_buffer,xlocal); } else /* Else, if stdin redirected... */ { /* Just assume local, so "set speed" and similar commands will work */ /* If not really local, how could it work anyway?... */ xlocal = 1; debug(F101," redirected stdin","",xlocal); } } /* line locking not needed -- os9 has exclusive access flag on open */ /* Got the line, now set the desired value for local. */ if (*lcl < 0) *lcl = xlocal; /* Get tty device settings */ _gs_opt(ttypn,&ttold); /* Get sgtty info */ _gs_opt(ttypn,&ttraw); /* And a copy of it for packets*/ _gs_opt(ttypn,&tttvt); /* And one for virtual tty service */ debug(F101,"ttopen, ttypn","",ttypn); debug(F101," lcl","",*lcl); return(0); } /* T T C L O S -- Close the TTY */ ttclos(foo) int foo; { if (ttypn < 0) return(0); /* Wasn't open. */ if (xlocal) { if (tthang()) /* Hang up phone line */ fprintf(stderr,"Warning, problem hanging up the phone\n"); } ttres(); /* Reset modes. */ /* Relinquish exclusive access if we might have had it... */ close(ttypn); /* Close it. */ debug (F101,"closed connection, ttypn","",ttypn); ttypn = -1; /* Mark it as closed. */ return(0); } #ifdef MYREAD /* Private buffer for myread() and its companions. Not for use by anything * else. ttflui() is allowed to reset them to initial values. ttchk() is * allowed to read my_count. * * my_item is an index into mybuf[]. Increment it *before* reading mybuf[]. * * A global parity mask variable could be useful too. We could use it to * let myread() strip the parity on its own, instead of stripping sign * bits as it does now. */ #define MYBUFLEN 256 static CHAR mybuf[MYBUFLEN]; /* Buffer, including push back */ static int my_count = 0; /* Number of chars still in mybuf */ static int my_item = -1; /* Last index read from mybuf[] */ /* myread() -- Efficient read of one character from communications line. * * Uses a private buffer to minimize the number of expensive read() system * calls. Essentially performs the equivalent of read() of 1 character, which * is then returned. By reading all available input from the system buffers * to the private buffer in one chunk, and then working from this buffer, the * number of system calls is reduced in any case where more than one character * arrives during the processing of the previous chunk, for instance high * baud rates or network type connections where input arrives in packets. * If the time needed for a read() system call approaches the time for more * than one character to arrive, then this mechanism automatically compensates * for that by performing bigger read()s less frequently. If the system load * is high, the same mechanism compensates for that too. * * myread() is a macro that returns the next character from the buffer. If the * buffer is empty, mygetbuf() is called. See mygetbuf() for possible error * returns. * * This should be efficient enough for any one-character-at-a-time loops. * For even better efficiency you might use memcpy()/bcopy() or such between * buffers (since they are often better optimized for copying), but it may not * be worth it if you have to take an extra pass over the buffer to strip * parity and check for CTRL-C anyway. * * Note that if you have been using myread() from another program module, you * may have some trouble accessing this macro version and the private variables * it uses. In that case, just add a function in this module, that invokes the * macro. */ #define myread() (--my_count < 0 ? mygetbuf() : 255 & (int)mybuf[++my_item]) /* Specification: Push back up to one character onto myread()'s queue. * * This implementation: Push back characters into mybuf. At least one character * must have been read through myread() before myunrd() may be used. After * EOF or read error, again, myunrd() can not be used. Sometimes more than * one character can be pushed back, but only one character is guaranteed. * Since a previous myread() must have read its character out of mybuf[], * that guarantees that there is space for at least one character. If push * back was really needed after EOF, a small addition could provide that. * * myunrd() is currently not called from anywhere inside kermit... */ #ifdef NOTUSED myunrd(ch) CHAR ch; { if (my_item >= 0) { mybuf[my_item--] = ch; ++my_count; } } #endif /* mygetbuf() -- Fill buffer for myread() and return first character. * * This function is what myread() uses when it can't get the next character * directly from its buffer. First, it calls a system dependent myfillbuf() * to read at least one new character into the buffer, and then it returns * the first character just as myread() would have done. This function also * is responsible for all error conditions that myread() can indicate. * * Returns: When OK => a positive character, 0 or greater. * When EOF => -2. * When error => -3, error code in errno. * * Older myread()s additionally returned -1 to indicate that there was nothing * to read, upon which the caller would call myread() again until it got * something. The new myread()/mygetbuf() always gets something. If it * doesn't, then make it do so! Any program that actually depends on the old * behaviour will break. * * The older version also used to return -2 both for EOF and other errors, * and used to set errno to 9999 on EOF. The errno stuff is gone, EOF and * other errors now return different results, although Kermit currently never * checks to see which it was. It just disconnects in both cases. * * Kermit lets the user use the quit key to perform some special commands * during file transfer. This causes read(), and thus also mygetbuf(), to * finish without reading anything and return the EINTR error. This should * be checked by the caller. Mygetbuf() could retry the read() on EINTR, * but if there is nothing to read, this could delay Kermit's reaction to * the command, and make Kermit appear unresponsive. * * The debug() call should be removed for optimum performance. */ int mygetbuf() { my_count = myfillbuf(); /* debug(F101, "myfillbuf read", "", my_count); */ if (my_count <= 0) return(my_count < 0 ? -3 : -2); --my_count; return(255 & (int)mybuf[my_item = 0]); } /* myfillbuf(): * System-dependent read() into mybuf[], as many characters as possible. * * Returns: OK => number of characters read, always more than zero. * EOF => 0 * Error => -1, error code in errno. * * If there is input available in the system's buffers, all of it should be * read into mybuf[] and the function return immediately. If no input is * available, it should wait for a character to arrive, and return with that * one in mybuf[] as soon as possible. It may wait somewhat past the first * character, but be aware that any such delay lengthens the packet turnaround * time during kermit file transfers. Should never return with zero characters * unless EOF or irrecoverable read error. * * Correct functioning depends on the correct tty parameters being used. * Better control of current parameters is required than may have been the * case in older Kermit releases. For instance, O_NDELAY (or equivalent) can * no longer be sometimes off and sometimes on like it used to, unless a * special myfillbuf() is written to handle that. Otherwise the ordinary * myfillbuf()s may think they have come to EOF. * * If your system has a facility to directly perform the functioning of * myfillbuf(), then use it. If the system can tell you how many characters * are available in its buffers, then read that amount (but not less than 1). * If the system can return a special indication when you try to read without * anything to read, while allowing you to read all there is when there is * something, you may loop until there is something to read, but probably that * is not good for the system load. */ /* This is for OSK. _gs_rdy returns the number * of characters available for reading. If none are available, wait * until something arrives, otherwise return all there is. * The OSK version needs to handle timeouts explicitly since * OSK alarm() does not interupt I/O. (Only done when filling buffer * to reduce overhead.) */ int myfillbuf() { register int avail; (void) remove_alarm(); /* let's fake it instead */ for(;;) { if (timowhen && time((time_t *)0) >= timowhen) { /*check for timeout*/ timowhen = 0; if (sigtbl[MAXSIG] != SIG_DFL) (*sigtbl[MAXSIG])(SIGALRM); return -1; } if ((avail = _gs_rdy(ttypn)) > 0) break; sigmask(1); _ss_ssig(ttypn, SIGARB); sleep(1); /* interupted by signal if incoming char */ _ss_rel(ttypn, SIGARB); } if (avail > MYBUFLEN) avail = MYBUFLEN; return(read(ttypn, mybuf, (int) avail)); } #endif /* MYREAD */ /* T T H A N G -- Hangup phone line */ tthang() { /* if (ttypn < 0) return(0); /* Not open. */ return 0; /* not implemented */ } /* T T R E S -- Restore terminal to "normal" mode. */ ttres() { /* Restore the tty to normal. */ if (ttypn < 0) return(-1); /* Not open. */ #ifdef XMODE if (xmode(ttold.sg_baud) < 0) return(-1); #endif tsleep(2); /* Wait for pending i/o to finish. */ if (_ss_opt(ttypn,&ttold) < 0) return(-1); /* Restore sgtty stuff */ return(0); } /* T T P K T -- Condition the communication line for packets. */ /* or for modem dialing */ /* If called with speed > -1, also set the speed. */ /* Returns 0 on success, -1 on failure. */ ttpkt(speed,flow,parity) long speed; int flow, parity; { int s; if (ttypn < 0) return(-1); /* Not open. */ if (speed >-1) if ((s=ttsspd(speed/10)) <0) return(-1); /* Check the speed */ ttprty = parity; ttraw.sg_case = ttraw.sg_backsp = ttraw.sg_delete = ttraw.sg_echo = ttraw.sg_alf = ttraw.sg_nulls = ttraw.sg_pause = ttraw.sg_bspch = ttraw.sg_dlnch = ttraw.sg_eorch = ttraw.sg_eofch = ttraw.sg_rlnch = ttraw.sg_dulnch = ttraw.sg_psch = ttraw.sg_kbich = ttraw.sg_kbach = ttraw.sg_bsech = ttraw.sg_tabcr = 0; if (speed >-1) ttraw.sg_baud = (char)s; if (flow==1) { ttraw.sg_xon = 0x11; ttraw.sg_xoff = 0x13; } else ttraw.sg_xon = ttraw.sg_xoff = 0; if(_ss_opt(ttypn,&ttraw) < 0) return(-1); /* set new modes . */ ttflui(); /* Flush any pending input */ return(0); } /* T T V T -- Condition communication line for use as virtual terminal */ ttvt(speed,flow) long speed; int flow; { int s; if (ttypn < 0) return(-1); /* Not open. */ if ((s=ttsspd(speed/10)) <0) return(-1); /* This speed not supported */ tttvt.sg_case = tttvt.sg_backsp = tttvt.sg_delete = tttvt.sg_echo = tttvt.sg_alf = tttvt.sg_nulls = tttvt.sg_pause = tttvt.sg_bspch = tttvt.sg_dlnch = tttvt.sg_eorch = tttvt.sg_eofch = tttvt.sg_rlnch = tttvt.sg_dulnch = tttvt.sg_psch = tttvt.sg_kbich = tttvt.sg_kbach = tttvt.sg_bsech = tttvt.sg_tabcr = 0; tttvt.sg_baud = (char)s; if (flow==1) { tttvt.sg_xon = 0x11; tttvt.sg_xoff = 0x13; } else tttvt.sg_xon = tttvt.sg_xoff = 0; s = _ss_opt(ttypn,&tttvt); /* set new modes . */ debug (F101,"ss_opt on tty was :","",s); return(0); } /* T T F L U I -- Flush tty input buffer */ ttflui() { int n; char flushbuf[256]; if (ttypn < 0) return(-1); /* Not open. */ #ifdef MYREAD my_count = 0; /* initialize myread() stuff */ my_item = -1; #endif while((n=_gs_rdy(ttypn))>0) read(ttypn, flushbuf, n>256 ? 256 : (unsigned int)n); return 0; } /* T T C H K -- Tell how many characters are waiting in tty input buffer */ ttchk() { register int n; n = _gs_rdy(ttypn); if (n < 0) n = 0; #ifdef MYREAD if (my_count > 0) n += my_count; #endif return n; } /* T T X I N -- Get n characters from tty input buffer */ /* Returns number of characters actually gotten, or -1 on failure */ /* Intended for use only when it is known that n characters are actually */ /* Available in the input buffer. */ ttxin(n,buf) int n; CHAR *buf; { register int c; #ifdef MYREAD register CHAR *bp, *bpe; #else register int x; #endif ttpmsk = (ttprty) ? 0177 : 0377; /* Parity stripping mask. */ debug(F101,"ttxin n","",n); #ifdef MYREAD for( bpe = (bp = buf) + n; (bp != bpe) && (c = myread()) >= 0; ) *bp++ = c & ttpmsk; if (c < 0) return -1; *bp = '\0'; return n; #else x = read(ttypn,(char *)buf,(unsigned int)n); if (ttprty) { for (c = 0; c < n; c++) buf[c] &= 0177; } if (x > 0) buf[x] = '\0'; if (x < 0) x = -1; return(x); #endif } /* C O N I N T -- Console Interrupt setter */ VOID conint(f,s) SIGTYP (*f)(), (*s)(); { if (backgrd) return; /* must ignore signals in bkgrd */ signal(SIGINT,f); /* console escape in pkt modes */ } /* C O N N O I -- Reset console terminal interrupts */ VOID connoi() { /* Console-no-interrupts */ signal(SIGQUIT,(fptr)SIG_IGN); signal(SIGINT,(fptr)SIG_IGN); } /* T T O L -- Similar to "ttinl", but for writing. */ ttol(s,n) int n; CHAR *s; { int x; debug(F101,"ttol: ttypn","",ttypn); if (ttypn < 0) return(-1); /* Not open. */ x = write(ttypn,s,(unsigned int)n); debug(F111,"ttol",s,n); if (x < 0) debug(F101,"ttol failed","",x); return(x); } /* T T O C -- Output a character to the communication line */ int #ifdef CK_ANSIC ttoc(char c) #else ttoc(c) char c; #endif /* CK_ANSIC */ /* ttoc */{ if (ttypn < 0) return(-1); /* Not open. */ return(write(ttypn,&c,1)); } /* T T I N L -- Read a record (up to break character) from comm line. */ /* Reads up to "max" characters from the communication line, terminating on the packet-end character (eol), or timing out and returning -1 if the eol character not encountered within "timo" seconds. The characters that were input are copied into "dest" with their parity bits stripped if parity was selected. Returns the number of characters read. Characters after the eol are available upon the next call to this function. The idea is to minimize the number of system calls per packet, and also to minimize timeouts. This function is the inner loop of the program and must be as efficient as possible. The current strategy is to use myread(). WARNING: this function calls parchk(), which is defined in another module. Normally, ckutio.c does not depend on code from any other module, but there is an exception in this case because all the other ck?tio.c modules also need to call parchk(), so it's better to have it defined in a common place. */ #define CTRLC '\03' int #ifdef PARSENSE #ifdef CK_ANSIC ttinl(CHAR *dest, int max,int timo, CHAR eol, CHAR start) #else ttinl(dest,max,timo,eol,start) int max,timo; CHAR *dest, eol, start; #endif /* CK_ANSIC */ #else /* not PARSENSE */ #ifdef CK_ANSIC ttinl(CHAR *dest, int max,int timo, CHAR eol) #else ttinl(dest,max,timo,eol) int max,timo; CHAR *dest, eol; #endif /* __SDTC__ */ #endif /* PARSENSE */ /* ttinl */ { CHAR ch; if (ttypn < 0) return(-1); /* Not open. */ debug(F101,"ttinl max","",max); debug(F101,"ttinl timo","",timo); *dest = '\0'; /* Clear destination buffer */ if (timo < 0) timo = 0; /* Safety */ if (setjmp(sjbuf)) { /* Timer went off? */ debug(F100,"ttinl timout","",0); /* Get here on timeout. */ debug(F110," with",(char *) dest,0); return(-1); /* and return error code. */ } else { register int i, n; /* local variables */ int ccn = 0; #ifdef PARSENSE int flag = 0; debug(F000,"ttinl start","",start); flag = 0; /* Start of packet flag */ #endif /* PARSENSE */ if (timo) { /* Don't time out if timo == 0 */ saval = signal(SIGALRM,timerh); /* Enable timer interrupt */ alarm((unsigned int)timo); /* Set it. */ } ttpmsk = (ttprty) ? 0177 : 0377; /* Set parity stripping mask. */ /* Now read into destination, stripping parity and looking for the */ /* the packet terminator, and also for two Ctrl-C's typed in a row. */ i = 0; /* Destination index */ debug(F101,"ttinl eol","",(int)eol); #ifdef MYREAD while (i < max-1) { debug(F101,"ttinl i","",i); if ((n = myread()) < 0) { debug(F101,"ttinl myread failure, n","",n); debug(F101,"ttinl myread errno,","",errno); break; } #else while ((i < max-1) && (n = read(ttyfd, &ch, 1)) > 0) { n = ch; #endif /* MYREAD */ #ifdef PARSENSE if ((flag == 0) && ((n & 0x7f) == start)) flag = 1; if (flag) dest[i++] = n & ttpmsk; #else dest[i++] = n & ttpmsk; #endif /* PARSENSE */ if ((n & 0x7f) == CTRLC) { /* Check for ^C^C */ if (++ccn > 1) { /* If we got 2 in a row, bail out. */ if (timo) { /* Clear timer. */ ttimoff(); } fprintf(stderr,"^C...\r\l"); /* Echo Ctrl-C */ return(-2); } } else ccn = 0; /* Not ^C, so reset ^C counter, */ #ifdef PARSENSE if (flag == 0) { debug(F101,"ttinl skipping","",n); continue; } #endif /* PARSENSE */ /* Check for end of packet */ if ((n & 0x7f) == eol) { debug(F101,"ttinl got eol","",(int)eol); dest[i] = '\0'; /* Yes, terminate the string, */ /* debug(F101,"ttinl i","",i); */ #ifdef PARSENSE /* Here's where we actually check and adjust the parity. */ /* The major flaw here is if parity is NONE (ttprty = 0) and the packets */ /* really do have no parity, then parchk() is called for every packet. */ /* In practice, this doesn't really harm efficiency noticably, but it would */ /* be better if ttinl() had a way of knowing to stop doing this once a */ /* particular file transfer had been started and checked. */ if (ttprty == 0) { if ((ttprty = parchk(dest,start,i)) > 0) { int j; debug(F101,"ttinl senses parity","",ttprty); debug(F110,"ttinl packet before",dest,0); ttpmsk = 0x7f; for (j = 0; j < i; j++) dest[j] &= 0x7f; /* Strip parity from packet */ debug(F110,"ttinl packet after ",dest,0); } else debug(F101,"parchk","",ttprty); } #endif /* PARSENSE */ if (timo) { /* Turn off timer. */ ttimoff(); } debug(F111,"ttinl got", dest,i); return(i); } } /* end of while() */ if (timo) /* Turn off timer. */ ttimoff(); return(-1); } } /* T T I N C -- Read a character from the communication line */ /* On success, returns the character that was read, >= 0. On failure, returns -3 on internal error, -2 on communication disconnect, -1 on timeout and other */ int ttinc(timo) int timo; { CHAR ch = 0; int n = 0; if (ttypn < 0) return(-1); /* Not open. */ if (timo <= 0) { /* Untimed. */ /* not realy untimed, ckudia.c uses alarm() */ #ifdef MYREAD /* comm line failure returns -1 thru myread, so no &= 0377 */ n = myread(); /* Wait for a character... */ /* debug(F101,"ttinc n","",n); */ #else while ((n = read(ttypn,&ch,1)) == 0) /* Wait for a character. */ /* Shouldn't have to loop in ver 5A. */ ; /* debug(F000,"ttinc","",ch); */ return( (n < 1) ? -3 : (ch & ttpmsk) ); #endif /* MYREAD */ } else { saval = signal(SIGALRM,timerh); /* Timed, set up timer. */ alarm((unsigned int)timo); if (setjmp(sjbuf)) { n = -1; } else { #ifdef MYREAD n = myread(); /* If managing own buffer... */ debug(F101,"ttinc myread","",n); #else n = timoread(ttypn,(char *)&ch,1); /*Otherwise call the system.*/ debug(F101,"ttinc read","",n); if (n > 0) n = ch & 255; else n = (n < 0) ? -3 : -2; /* Special return codes */ #endif /* MYREAD */ } ttimoff(); /* Turn off timer */ } return( (n < 0) ? n : (n & ttpmsk) ); /* Return masked char or neg. */ } /* T T S N D B -- Send a BREAK signal */ ttsndb() { struct sgbuf before; int i; if (ttypn < 0) return(-1); /* Not open. */ #ifndef XMODE if (send_break(ttypn) == 0) return(0); /* all fine done by driver */ if (errno!=E_UNKSVC) return(-1); /*should have been unknown service call*/ if (_gs_opt(ttypn,&before) < 0) return(-1); /* din't get old speed */ for (i=B50; i0) ? 1:0); } /* C O N X O -- Write x characters to the console terminal */ /*VOID*/ int conxo(x,s) char *s; int x; { write(1,s,(unsigned int)x); } /* C O N O L -- Write a line to the console terminal */ /*VOID*/ int conol(s) char *s; { int len; len = strlen(s); write(1,s,(unsigned int)len); } /* C O N O L L L -- Output a string followed by LF */ static VOID conolll(s) char *s; { conol(s); write(1,"\l",1); } /* C O N O L A -- Write an array of lines to the console terminal */ /*VOID */ int conola(s) char *s[]; { int i; for (i=0 ; *s[i] ; i++) conolll(s[i]); } /* C O N O L L -- Output a string followed by CRLF */ /*VOID*/ int conoll(s) char *s; { conol(s); write(1,"\r\l",2); } /* C O N C H K -- Return how many characters available at console */ conchk() { int x; x=_gs_rdy(0); if(x<0) x=0; /* always return 0 irrespective the type of error */ /* e.q. /nil would return E_UNKSVC and conchk is only */ /* checked on != 0 anyway, Bob Larson */ debug (F101,"conchk","",x); return x; } /* C O N I N C -- Get a character from the console */ int coninc(timo) int timo; { int n = 0; CHAR ch; if (timo <= 0 ) { /* Untimed. */ n = read(0, (char *)&ch, 1); /* Read a character. */ ch &= 0377; if (n > 0) /* Return the character if read ok */ return(ch); else /* otherwise... */ { debug(F101, "coninc(0) errno","",errno); return(-1); /* Return -1 as error indication */ } } if (setjmp(sjbuf)) n = -2; else { saval = signal(SIGALRM,timerh); /* Timed read, so set up timer */ alarm((unsigned int)timo); n = timoread(0, (char *)&ch, 1); ttimoff(); /* Turn off timer */ ch &= 0377; } if (n > 0) return(ch); else return(-1); } /* emulate unix signal functions */ fptr signal(sig,func) int sig; fptr func; { fptr temp; if (sig < MAXSIG) { temp = sigtbl[sig]; sigtbl[sig] = func; return temp; } if (sig == SIGALRM) { temp = sigtbl[MAXSIG]; sigtbl[MAXSIG] = func; return temp; } return (fptr)-1; } VOID catch(sig) register int sig;{ register fptr temp; if(sig==SIGARB) return; /* nothing to do with i/o signals */ if(sig < MAXSIG) { if ((temp=sigtbl[sig])==SIG_DFL) doexit(sig,-1); if(temp!=(SIGTYP (*)())SIG_IGN) (*temp)(sig); else { if (sig == SIGQUIT) /* output ceises on CTRL-E typed, so notify */ stdin->_flag |= _ERR; } return; } if(sig == SIGALRM) { if ((temp=sigtbl[MAXSIG])==SIG_DFL) return; if (temp!=(SIGTYP (*)())SIG_IGN) (*temp)(sig); return; } doexit(sig,-1); } int isatty(path) int path; { struct sgbuf buffer; return((_gs_opt(path,&buffer)<0) ? 0 : buffer.sg_class == 0); } /*********************************************************************/ int msleep(m_secs) /* sleeps at least !! (not exact) m_secs milli seconds */ register int m_secs; { register unsigned int i; register int tick_rate; if ((tick_rate = _getsys(D_TckSec,2)) < 0) return(-1); /* clock not on */ i = (unsigned int)(((double)m_secs*(double)tick_rate)/1000.0); tsleep(i+2); /* +1 for rounding and another +1 because tsleep is accurate not more than +/-(!) 1 tick */ return(0); } /**********************************************************************/ psuspend(foo) int foo; { return(-1); } long ttgspd() /* Get current tty speed */ { struct sgbuf buffer; if (ttypn < 0) return(-1); if(_gs_opt(ttypn,&buffer) < 0) return(-1); return(bdrts[buffer.sg_baud]); } /* T T S C A R R -- Set ttcarr variable, controlling carrier handling. * * 0 = Off: Always ignore carrier. E.g. you can connect without carrier. * 1 = On: Heed carrier, except during dialing. Carrier loss gives disconnect. * 2 = Auto: For "modem direct": The same as "Off". * For real modem types: Heed carrier during connect, but ignore * it anytime else. Compatible with pre-5A C-Kermit versions. * * As you can see, this setting does not affect dialing, which always ignores * carrier (unless there is some special exception for some modem type). It * does affect ttopen() if it is set before ttopen() is used. This setting * takes effect on the next call to ttopen()/ttpkt()/ttvt(). And they are * (or should be) always called before any communications is tried, which * means that, practically speaking, the effect is immediate. * * Of course, nothing of this applies to remote mode (xlocal = 0). * * Someone has yet to uncover how to manipulate the carrier in the BSD * environment (or any non-termio using environment). Until that time, this * will simply be a no-op for BSD. * * Note that in previous versions, the carrier was most often left unchanged * in ttpkt()/ttvt() unless they were called with DIALING or CONNECT. This * has changed. Now it is controlled by ttcarr in conjunction with these * modes. */ ttscarr(carrier) int carrier; { ttcarr = carrier; debug(F101, "ttscarr","",ttcarr); return(ttcarr); } /* T T G M D M -- Get modem signals */ /* Looks for the modem signals CTS, DSR, and CTS, and returns those that are on in as its return value, in a bit mask as described for ttwmdm. Returns: -3 Not implemented -2 if the line does not have modem control -1 on error. >= 0 on success, with a bit mask containing the modem signals that are on. */ ttgmdm() { return(-3); } /* T T S S P D -- Set the transmission of tty to ten times the argument */ ttsspd(speed) int speed; { int s; struct sgbuf before; debug (F101,"ttsspd: speed(cps):","",speed); if (ttypn < 0) return(-1); switch (speed) { case 5: s = B50; break; case 7: s = B75; break; case 11: s = B110; break; case 13: s = B134; break; case 15: s = B150; break; case 30: s = B300; break; case 60: s = B600; break; case 120: s = B1200; break; case 180: s = B1800; break; case 200: s = B2000; break; case 240: s = B2400; break; case 360: s = B3600; break; case 480: s = B4800; break; case 720: s = B7200; break; case 960: s = B9600; break; case 1920: s = B19200; break; case 3840: s = B38400; break; case 888: return(-1); /* no 75/1200 split speed */ default: return -1; } _gs_opt(ttypn,&before); before.sg_baud = (char)s; #ifdef XMODE xmode((char)s); /* xmode open and closes => new options => restore as they were before */ return(_ss_opt(ttypn,&before) < 0 ? -1 : s); #else return(_ss_opt(ttypn,&before) < 0 ? -1 : s); #endif } #ifdef XMODE int /* change the line tty speed via xmode, special hack for bad drivers*/ xmode(os9_speed) char os9_speed; { char command[DEVNAMLEN+17]; /* e.g. xmode baud=38400 = 17 chars */ char devicename[DEVNAMLEN]; long old_speed; if ((old_speed = ttgspd()) < 0) return(-1); devicename[0] = '/'; _gs_devn(ttypn,&devicename[1]); sprintf(command,"xmode %s baud=%d",devicename,bdrts[os9_speed]); debug (F110,"xmode command = ",command,0); debug (F101,"xmode baud","",os9_speed); zsyscmd(command); close(ttypn); /* hopefully not iniz'ed */ if ((ttypn=open(devicename,0x43)) < 0) /* no open with new speed anymore*/ { sprintf(command,"xmode %s baud=%d",devicename,old_speed); zsyscmd(command); /* restore old speed */ ttypn=open(devicename,0x43); /* now it should work again */ return (-1); } return(os9_speed); } #endif VOID ttimoff() { /* Turn off any timer interrupts */ alarm(0); if (saval) signal(SIGALRM,saval); else signal(SIGALRM,(fptr)SIG_DFL); saval = NULL; } int alarm(secs) unsigned int secs; /* after secs seconds the signal SIGALRM will be sent */ /* 0 will clear the alarm */ { if (secs) { timowhen = time((time_t *)0) + secs; return(add_alarm(secs,SIGALRM)); } timowhen = 0; return(remove_alarm()); } /* read from serial device with working timeout */ int timoread(ttypn, cp, n) int ttypn; char *cp; int n; { if (timowhen == 0) return read(ttypn, cp, n); (void) remove_alarm(); for(;;) { if (_gs_rdy(ttypn) >= n) return read(ttypn, cp, n); if (time((time_t *)0) >= timowhen) { timowhen = 0; if (sigtbl[MAXSIG] != SIG_DFL) (*sigtbl[MAXSIG])(SIGALRM); return -1; } _ss_ssig(ttypn, SIGARB); sleep(1); /* interupted by _ss_ssig if incoming char */ _ss_rel(ttypn, SIGARB); } } /* Timeout handler for communication line input functions */ static SIGTYP timerh(sig) int sig;{ ttimoff(); sigmask(-1); /* we are in an intercept routine but do not perform a F$RTE (done implicitly but rts). => we have to decrement the sigmask as F$RTE does. Warning: longjump only restores the cpu registers NOT the fpu registers. So, don't use fpu at all or at least don't use common fpu (double or float) register variables */ longjmp(sjbuf,1); } int ustrncmp(a2,a3,d4) /*string compare but ignore upper and lower case*/ register char *a2,*a3; register int d4; { while((--d4>=0)&&(_toupper(*a2)==_toupper(*a3))) { if (*a3++ == '\0') return 0; a2++; } return (d4 < 0) ? 0 : *a2-*a3; }