char *ckxv = "Data General tty I/O, 7.1.106, 22 Jul 2000"; /* C K D T I O */ /* C-Kermit interrupt, terminal control & i/o functions for DG AOS/VS */ /* Author: Frank da Cruz Columbia University Academic Information Systems, New York City. Copyright (C) 1985, 2000, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. This module originally adapted to AOS/VS by: Phil Julian, SAS Institute, Inc., Box 8000, Cary, NC 27512-8000, circa 1985. Adapted to C-Kermit 5A by Eugenia Harris of Data General, 1992. Upgraded to C-Kermit 6.0 by Frank da Cruz, 1996; 7.0 (1998); 7.1 (2000). */ #define MAXINBUF 2048 /* See ckdcon.c for same definition. */ #nolist #include #include #include #include #include #include #include #include #include #include #include #include #list #define CONINT_TSK 20 /* Task id of interrupt-checker */ #define CONINT_PRI 0 /* Same as calling task */ #define CONINT_STACK 02000 int con_reads_mt = 0; /* Flag if task is active */ int conint_ch; /* MT character read */ int conint_avl; /* Flag that char available */ int termtype; /* Delimiter table -- defaults to CR only */ short idel_tbl[7] = {0x4,0,0,0,0,0,0}; #include /* Unix Standard i/o */ #ifndef DEVNAMLEN #define DEVNAMLEN (L_ctermid + 6) #endif #ifdef NETCONN #undef DEVNAMLEN #define DEVNAMLEN 50 /* longer field for host:service */ #endif /* NETCONN */ #include /* Directory */ #include /* Character types */ /* Maximum length for the name of a tty device */ #ifndef DEVNAMLEN #define DEVNAMLEN 25 #endif #include /* Unix Standard i/o */ #include /* Interrupts */ #include /* Longjumps */ #include "ckcdeb.h" /* Typedefs, formats for debug() */ #include "ckcnet.h" #include char *ckxsys = " Data General AOS/VS"; /* Features... */ #define MYREAD /* 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. flfnam -- Name of lock file, including its path, e.g., "/usr/spool/uucp/LCK..cul0" or "/etc/locks/tty77" haslock -- Flag set if this kermit established a uucp lock. inbufc -- number of tty line rawmode unread characters (system III/V unixes) backgrd -- Flag indicating program executing in background ( & on end of shell command). Used to ignore INT and QUIT signals. /* Declarations of variables global within this module */ /* Declarations of variables global within this module */ #ifdef TTLEBUF /* See ckcnet.h */ int ttpush = -1; #define LEBUFSIZ 4096 static CHAR le_buf[LEBUFSIZ]; static int le_start = 0, le_end = 0, le_data = 0; #endif /* TTLEBUF */ static time_t tcount; /* Elapsed time counter */ SIGTYP (*saval)() = NULL; /* For saving alarm handler */ /* The following lists of functions are somewhat out of date. For a better listing, see ckutio.c Functions for assigned communication line (either external or console tty): sysinit() -- System dependent program initialization ttopen(ttname,local,mdmtyp) -- Open the named tty for exclusive access. ttclos() -- Close & reset the tty, releasing any access lock. ttpkt(speed,flow,parity)-- 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,eol,start) -- Timed read line from the tty. ttinc(timo) -- Timed read character from tty. myread() -- System 3 raw mode bulk buffer read, gives -- subsequent chars one at a time and simulates -- FIONREAD! 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. ttfluo() -- Flush tty output buffer. (not used) ttgmdm() -- Returns status of modem signals (not used) ttgspd() -- Returns speed of tty line ttsspd(cps) -- Set line speed on tty line ttimoff() -- Turns off all pending timer interrupts ttscarr(carrier) -- Copies argument to global variable ttwmdm(mdmsig,timo) -- Wait for specified signal (not used) ttlock(ttname) -- Lock against uucp collisions (not used) ttunlck() -- Unlock " " " (not used) look4lk(ttname) -- Check if a lock file exists (not used) 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. congks(timo) -- Get keyboard scan code (not used) Time functions: msleep(m) -- Millisecond sleep ztime(&s) -- Return pointer to date/time string rtimer() -- Reset timer gtimer() -- Get elapsed time since last call to rtimer() */ #ifdef UXIII #ifdef datageneral /* Console and terminal characteristics vectors */ short *charcurr; short charconin[$CLMAX]; /* Console input; channel 0 */ short charconout[$CLMAX]; /* Console output; channel 1 */ short chartty[$CLMAX]; /* generic tty channel */ short charttyin[$CLMAX]; /* tty input; channel ttyfd */ short charttyout[$CLMAX]; /* tty output; channel ttyfdout */ short ch_ttold[$CLMAX]; short ch_ttraw[$CLMAX]; short ch_ttvt[$CLMAX]; short ch_ccold[$CLMAX]; short ch_ccraw[$CLMAX]; short ch_ccbrk[$CLMAX]; #undef ECHO #include /* Add synonyms for Unix type ioctl */ #define TCSETAF TCSETA #define TCSETAW TCSETA #else #include #include #endif datageneral #include /* directory reading for locking */ #include /* error numbers for system returns */ #endif /* UXIII */ /* Declarations */ long time(); /* All Unixes should have this... */ extern int errno; /* System call error return */ extern int ttnproto; extern int ttnet; extern int xfrcan, xfrchr, xfrnum; /* Transfer cancellation */ /* 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 = CTTNAM; /* Remote by default, use normal */ int dfloc = 0; /* controlling terminal name. */ int dfprty = 0; /* Parity (0 = none) */ int dfflow = 1; /* Xon/Xoff flow control */ int backgrd = 0; /* Assume in foreground. */ int ckxech = 0; /* 0 if system normally echoes console characters, else 1 */ /* Declarations of variables global within this module */ static long tcount; /* Elapsed time counter */ static char *brnuls = "\0\0\0\0\0\0\0"; /* A string of nulls */ static jmp_buf sjbuf, jjbuf; /* Longjump buffer */ static int lkf = 0, /* Line lock flag */ conif = 0, /* Console interrupts on/off flag */ cgmf = 0, /* Flag that console modes saved */ xlocal = 0; /* Flag for tty local or remote */ curcarr = 0; /* Carrier mode: require/ignore. */ int ttyfd = -1; /* TTY file descriptor */ int tvtflg = 0; long ttspeed = -1; /* For saving speed */ int ttflow = -9; /* For saving flow */ int ttld = -1; /* Line discipline */ int ttfdflg = 0; int ttpmsk = 0377; int ttprty = 0; int ttpflg = 0; /* Parity not sensed yet */ int ttmdm = 0; int ttcarr = CAR_AUT; /* Carrier handling mode. */ int telnetfd = 0; static int netconn = 0; int fdflag = 0; static char escchr; /* Escape or attn character */ static int kerld = 0; /* Not selected, no special l.d. */ int x25fd = 0; #ifdef UXIII static struct termio /* sgtty info... */ ttold, ttraw, tttvt, /* for communication line */ ccold, ccraw, cccbrk; /* and for console */ #else static struct sgttyb /* sgtty info... */ ttold, ttraw, tttvt, ttbuf, /* for communication line */ ccold, ccraw, cccbrk; /* and for console */ #endif char flfnam[80]; /* uucp lock file path name */ int haslock = 0; /* =1 if this kermit locked uucp */ static int inbufc = 0; /* stuff for efficient SIII raw line */ static int ungotn = -1; /* pushback to unread character */ static int conesc = 0; /* set to 1 if esc char (^\) typed */ static int ttlock(); /* definition of ttlock subprocedure */ static int ttunlck(); /* and unlock subprocedure */ static char ttnmsv[DEVNAMLEN]; /* copy of open path for tthang */ #ifdef datageneral FILE *ttfile,*ttfileout; /* Files opened by ttopen */ struct p_nio_ex w_io_parms; /* ?write system call structure */ struct p_nio_ex r_io_parms; /* ?read system call structure */ struct p_nio_ex io_parms; /* open structure in ttopen */ struct p_nio_ex x_io_parms; /* ?read/?write structure for ttyfd */ struct p_nio_ex xout_parms; /* ?write structure for ttyfdout */ struct p_screen x_io_scrn, r_io_scrn,io_screen; /* Edit-read structures */ int ttyfdout = -1; /* ?write Unix number for remote out */ short timotty = 0; /* {Flag for timeouts (=1), value */ short timocon = 0; /* tty is ttyfd..., con is Unix 0/1 */ extern char *SPACMD; /* returns space in CURRENT dir */ #endif /* Timeout handler for communication line input functions */ SIGTYP timerh(foo) int foo; { /* we will try to use this */ ttimoff(); longjmp(sjbuf,1); } /* Control-C trap for communication line input functions */ /* These are not used in the VS implementation. Instead, there is a task */ /* called conint_mt that reads characters from the keyboard and sets a */ /* flag when it gets one. It is then up to other routines (e.g., chkint()*/ /* to determine what to do with the characters (and zero the flag) --ENH */ int cc_int; /* Flag */ SIGTYP (* occt)(); /* For saving old SIGINT handler */ SIGTYP cctrap(foo) int foo; { /* Needs arg for ANSI C */ cc_int = 1; /* signal() prototype. */ return; } #ifdef TTLEBUF VOID le_init() { /* LocalEchoInit() */ int i; for (i = 0; i < LEBUFSIZ; i++) le_buf[i] = '\0'; le_start = 0; le_end = 0; le_data = 0; } VOID le_clean() { /* LocalEchoCleanup() */ le_init(); return; } int le_inbuf() { int rc = 0; if (le_start != le_end) { rc = (le_end - le_start + LEBUFSIZ) % LEBUFSIZ; } debug(F111,"le_inbuf","chars waiting",rc); return(rc); } int #ifdef CK_ANSIC le_putchar(char ch) #else le_putchar(ch) char ch; #endif /* CK_ANSIC */ /* le_putchar */ { if ((le_start - le_end + LEBUFSIZ)%LEBUFSIZ == 1) { debug(F110,"le_putchar","buffer is full",0); return(-1); } le_buf[le_end++] = ch; if (le_end == LEBUFSIZ) le_end = 0; le_data = 1; return(0); } int #ifdef CK_ANSIC le_puts(CHAR * s, int n) #else le_puts(s,n) CHAR * s; int n; #endif /* CK_ANSIC */ /* le_puts */ { int rc = 0; int i = 0; CHAR * p = (CHAR *)"le_puts"; hexdump(p,s,n); for (i = 0; i < n; i++) rc = le_putchar((char)s[i]); debug(F101,"le_puts","",rc); return(rc); } int #ifdef CK_ANSIC le_putstr(CHAR * s) #else le_putstr(s) CHAR * s; #endif /* CK_ANSIC */ /* le_puts */ { CHAR * p; int rc = 0; p = (CHAR *)"le_putstr"; hexdump(p,s,(int)strlen((char *)s)); for (p = s; *p && !rc; p++) rc = le_putchar(*p); return(rc); } int #ifdef CK_ANSIC le_getchar(CHAR * pch) #else /* CK_ANSIC */ le_getchar(pch) CHAR * pch; #endif /* CK_ANSIC */ /* le_getchar */ { int rc = 0; if (le_start != le_end) { *pch = le_buf[le_start]; le_buf[le_start] = 0; le_start++; if (le_start == LEBUFSIZ) le_start = 0; if (le_start == le_end) { le_data = 0; } rc++; } else { *pch = 0; } return(rc); } #endif /* TTLEBUF */ /* S Y S I N I T -- System-dependent program initialization. */ int sysinit() { extern char uidbuf[]; #ifdef datageneral /* Set up many things on the DG system, for initialization, etc. */ short flags[3]; char *username; int err; signal(SIGALRM,SIG_IGN); /* Get the terminal type, since DG terminals are different from others, especially in the way BS is handled ('\b' is EM). termtype = 0 means a regular DG style terminal, and termtype = 1 means an ANSI style terminal with ^H for backspace. If Kermit is run in batch or has redirected input, then we do not want to test or set the console characteristics. */ termtype = 0; if (isatty(0)) { if (sys_gchr(channel(0),1<<31,&flags)) { perror("sysinit: sys_gchr: "); return(1); } /* Only determine whether or not it is a $TTY */ if ((flags[1] & $DTYPE) == $TTY) termtype = 1; } zero((char *) &w_io_parms, sizeof(w_io_parms)); w_io_parms.ich = channel(1); /* stdout */ w_io_parms.isti = $IBIN|$RTDS|$ICRF|$OFOT; w_io_parms.isti &= ~$IPST; w_io_parms.imrs = MAXINBUF-1; w_io_parms.ibad = -1; w_io_parms.ircl = -1; zero((char *) &r_io_parms, sizeof(r_io_parms)); r_io_parms.ich = channel(0); /* stdin */ r_io_parms.isti = $IBIN|$RTFX|$ICRF|$OFIN; r_io_parms.isti &= ~$IPST; r_io_parms.imrs = MAXINBUF-1; r_io_parms.ibad = -1; r_io_parms.ircl = -1; zero((char *) &r_io_scrn, sizeof(r_io_scrn)); r_io_parms.etsp = 0; /* Screen management extension */ r_io_scrn.esfc &= ~$ESNE; /* Echo input data */ r_io_scrn.esfc &= ~$ESSE; /* No screen edit */ r_io_scrn.esfc |= $ESGT; /* Dump ring buffer */ zero((char *) &x_io_parms, sizeof(x_io_parms)); /* $RTDY must be used instead of $RTDS since this causes failure on * the Prime, which is probably getting data overruns on output or * missing data. Also, $RTDY is more efficient and faster. */ x_io_parms.isti = $IBIN|$RTFX|$ICRF|$OFIO|$IPKL; x_io_parms.isti &= ~$IPST; x_io_parms.imrs = MAXINBUF-1; x_io_parms.ibad = -1; x_io_parms.ircl = -1; /* IDEL would default to NULL, FF, CR, NL as delimiters. * The only delimiters here are CR and NL, for data sensitive * records. */ idel_tbl[0] = 0x24; x_io_parms.idel = idel_tbl; zero((char *) &x_io_scrn, sizeof(x_io_scrn)); x_io_parms.etsp = &x_io_scrn; /* Screen management extension */ x_io_parms.etsp |= BIT0; /* Turn on the high bit */ x_io_scrn.esfc &= ~$ESNE; /* Echo input data */ x_io_scrn.esfc &= ~$ESSE; /* No screen edit */ x_io_scrn.esfc |= $ESGT; /* Dump ring buffer */ memcpy((char *) &xout_parms,(char *) &x_io_parms,sizeof(x_io_parms)); xout_parms.isti = $IBIN|$RTDY|$ICRF|$OFOT; xout_parms.etsp = 0; /* Screen management extension */ username = getenv("LOGNAME"); ckstrncpy(uidbuf,username,UIDBUFLEN); /* Zero the console characteristics arrays */ zero ((char *) charconin, sizeof(charconin)); zero ((char *) charconout, sizeof(charconout)); zero ((char *) chartty, sizeof(chartty)); zero ((char *) charttyin, sizeof(charttyin)); zero ((char *) charttyout, sizeof(charttyout)); /* Initialize charconin & out, * and set up the console for CHAR/BREAK=BMOB */ if (sys_gechr( channel(0), (1<<31)|$CLMAX, charconin)) { perror("sysinit: gechr 0"); return(-1); } if (sys_gechr( channel(1), (1<<31)|$CLMAX, charconout)) { perror("sysinit: gechr 1"); return(-1); } charconin[4] &= ~$CBKM; charconin[4] |= $CBBM; if (sys_sechr( channel(0), (1<<31)|$CLMAX, charconin)) { perror("sysinit: sechr stdin "); return(-1); } if (sys_sechr( channel(1), (1<<31)|$CLMAX, charconout)) { perror("sysinit: sechr stdout "); return(-1); } #ifdef TTLEBUF le_init(); #endif /* TTLEBUF */ return(0); } char * whoami() { extern char uidbuf[]; return(uidbuf[0] ? (char *)uidbuf : "UNKNOWN"); } /* S E T T O -- set timeout on the channel to period. Save the old characteristics in char***in[]. */ /* AOS/VS device timeouts are used sparingly in this implementation of */ /* Kermit. In most cases, signals are used instead. --ENH */ int setto(chan, period) int chan, period; { int isconsole = 0; int iscon[2]; /* Flags because i/o could be redirected */ /* Setting is done external to here, since they can distinguish the console from ttyfd device. Both console channels must be set the same. */ if ( (chan == channel(0)) || (chan == channel(1)) ) { isconsole = 1; zero((char *) iscon, sizeof(iscon)); chan = channel(0); if (sys_gechr( channel(0), (1<<31)|$CLMAX, charconin)) if (isatty(0)) { iscon[0] = 1; perror("setto: gechr 0: "); return(-1); } charcurr = charconin; if (sys_gechr( channel(1), (1<<31)|$CLMAX, charconout)) if (isatty(1)) { iscon[1] = 1; perror("setto: gechr 1: "); return(-1); } timocon = period; } else { if (sys_gechr( chan, (1<<31)|$CLMAX, chartty)) { perror("setto: gechr tty: "); return(-1); } charcurr = chartty; timotty = period; } charcurr[1] |= (0100000>>$CTO); if (sys_sechr( chan, (1<<31)|$CLMAX, charcurr)) if ((isconsole == 0) || iscon[0]) { perror("setto: sechr: "); return(-1); } if (sys_stom( chan, 1<<31, period)) if ((isconsole == 0) || iscon[0]) { perror("setto: stom: "); return(-1); } if (isconsole == 0) return(0); chan = channel(1); charconout[1] |= (0100000>>$CTO); if (sys_sechr( chan, (1<<31)|$CLMAX, charconout)) if (iscon[1]) { perror("setto: sechr 1: "); return(-1); } if (sys_stom( chan, 1<<31, period)) if (iscon[1]) { perror("setto: stom 1: "); return(-1); } return(0); } /* R E S T O -- reset timeouts to the previous values, and restore char */ int resto(chan) int chan; { int i, chan0, chan1, iscon[2]; if ( (chan==channel(0)) || (chan==channel(1)) ) { chan0 = channel(0); chan1 = channel(1); iscon[0] = isatty(0); iscon[1] = isatty(1); charconin[1] &= ~(0100000>>$CTO); if (sys_stom( chan0, 1<<31, -1)) if (iscon[0]) { perror("resto: stom 0: "); return(-1); } for (i = 0; i < $CLMAX; i++) if (charconin[i] != 0) { if (sys_sechr( chan0, (1<<31)|$CLMAX, charconin)) if (iscon[0]) { perror("resto: sechr 0: "); return(-1); } break; } charconout[1] &= ~(0100000>>$CTO); if (sys_stom( chan1, 1<<31, -1)) if (iscon[1]) { perror("resto: stom 1: "); return(-1); } for (i = 0; i < $CLMAX; i++) if (charconout[i] != 0) { if (sys_sechr( chan1, (1<<31)|$CLMAX, charconout)) if (iscon[1]) { perror("resto: sechr 1: "); return(-1); } break; } timocon = 0; } else { chartty[1] &= ~(0100000>>$CTO); if (sys_stom( chan, 1<<31, -1)) { perror("setto: stom tty: "); return(-1); } for (i = 0; i < $CLMAX; i++) if (chartty[i] != 0) { if (sys_sechr( chan, (1<<31)|$CLMAX, chartty)) { perror("resto: sechr tty: "); return(-1); } break; } timotty = 0; } return(0); } /* S E T F L O W -- Set flow control in the device. The device is assumed to be an intelligent IAC, where a CHAR/ON/IFC/OFC could be done. */ static int dgsetflow(chan) int chan; { short termparms[$CLMAX]; if (sys_gechr( chan, (1<<31)|$CLMAX, termparms)) { perror("dgsetflow: gechr "); return(-1); } bitset(&termparms[4],$XOFC,1); bitset(&termparms[4],$XIFC,1); if (sys_sechr( chan, (1<<31)|$CLMAX, termparms)) { perror("dgsetflow: sechr "); return(-1); } return(0); } /* R E S F L O W -- Re-Set flow control in the device. The device is assumed to be an intelligent IAC, where a CHAR/OFF/IFC/OFC could be done. */ int resflow(chan) int chan; { int i; short termparms[$CLMAX]; if (sys_gechr( chan, (1<<31)|$CLMAX, termparms)) { perror("resflow: gechr "); return(-1); } bitset(&termparms[4],$XOFC,0); bitset(&termparms[4],$XIFC,0); if (sys_sechr( chan, (1<<31)|$CLMAX, termparms)) { perror("resflow: sechr "); return(-1); } return(0); } /* S E T S P E E D -- Set the baud rate when not possible thru IOCTL. The device is assumed to be an intelligent IAC, where a CHAR/ON/BAUD= could be done. */ int setspeed(sp) int sp; { short termparms[$CLMAX]; /* ttyfd is the Unix file number for the tty device. Setting the * characteristics will set it for both ttyfd and ttyfdout, since * it is the device itself which is affected, and not just the * channel. */ if (sys_gechr( channel(ttyfd), (1<<31)|$CLMAX, termparms)) { perror("setspeed: gechr "); return(-1); } termparms[3] &= ~$BRMSK; /* Zero out the previous setting */ termparms[3] |= sp; if (sys_sechr( channel(ttyfd), (1<<31)|$CLMAX, termparms)) { perror("setspeed: sechr "); return(-1); } return(0); } #endif datageneral /* S Y S C L E A N U P -- System-dependent cleanup */ int syscleanup() { return(0); } /* T T O P E N -- Open a tty for exclusive access. */ /* 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 /* 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. */ int ttopen(ttname,lcl,modem,timo) char *ttname; int *lcl, modem,timo; { /* AOS/VS special stuff */ FILE *dg_open(); /* Custom file open */ int retrycnt = 0; /* Retry if Modem disc in progress */ char *ctermid(); /* Wish they all had this! */ char *x; extern char* ttyname(); char cname[DEVNAMLEN+4]; #define NAMEFD #ifdef NAMEFD int i, j; char *p; debug(F101,"ttopen telnetfd","",telnetfd); #endif /* NAMEFD */ debug(F111,"ttopen entry modem",ttname,modem); debug(F101," ttyfd","",ttyfd); debug(F101," lcl","",*lcl); if (ttyfd > -1) { if (strncmp(ttname,ttnmsv,DEVNAMLEN)) /* are old & new the same? */ ttclos(ttyfd); /* no? - close old */ else /* yes? - ignore this call */ return(0); /* and return */ } #ifdef NETCONN if (modem < 0) { /* means special code for net type */ int x; ttmdm = modem; modem = -modem; /* negative value is net type */ fdflag = 0; /* stdio not redirected */ ttyfdout = -1; /* only use ttyfd for nets */ netconn = 1; /* network connection */ debug (F111,"ttopen net",ttname,modem); #ifdef NAMEFD for (p = ttname; isdigit(*p); p++) ; /* check for all digits */ if (*p == '\0' && telnetfd) { ttyfd = atoi(ttname); /* can we test if it's open? */ ttfdflg = 1; /* we got an open file descriptor */ debug(F111,"ttopen got open network fd",ttname,ttyfd); strncpy(ttnmsv,ttname,DEVNAMLEN); /* remember the "name" */ x = 1; /* return code is "good" */ if (telnetfd) { ttnet = NET_TCPB; ttnproto = NP_TELNET; } } else { #endif /* NAMEFD */ /* hostname or address */ x = netopen(ttname,lcl,modem); /* (see ckcnet.h) */ if (x > -1) { strncpy(ttnmsv,ttname,DEVNAMLEN); } else netconn = 0; #ifdef NAMEFD } #endif NAMEFD /* ENH - left out ifdef sony_news section */ xlocal = *lcl = 1; /* network connections are local */ debug(F101,"ttopen net x","",x); #ifdef COMMENT /* Let netopen() handle this. */ if ((x > -1) && (!x25fd)) x = tn_ini(); /* initialize telnet protocol */ #endif /* COMMENT */ return(x); } else { /* terminal device */ #endif /* NETCONN */ #ifdef NAMEFD /* This code lets you give Kermit an open file descriptor for a serial communication device, rather than a device name. Kermit assumes that the line is already open, locked, conditioned with the right parameters, etc. */ for (p = ttname; isdigit(*p); p++) ; /* check for all digits */ if (*p == '\0') { ttyfd = atoi(ttname); /* can we test if open? */ debug(F111,"ttopen got open fd",ttname,ttyfd); strncpy(ttnmsv,ttname,DEVNAMLEN); /* remember the "name" */ xlocal = *lcl = 1; /* assume it's local */ netconn = 0; /* assume not network */ tvtflg = 0; /* initialize modes? */ ttmdm = modem; /* remember modem type */ fdflag = 0; /* stdio not redirected */ ttfdflg = 1; /* flag we were opened */ /* this way */ /* ENH - left out ifdef sony_news */ return(0); /* return success */ } #endif /* NAMEFD */ #ifdef NETCONN } #endif /* NETCONN */ /* Here we have to open a serial device of the given name */ tvtflg = 0; /* flag for use by ttvt() */ /* 0 means ttvt() not called yet */ fdflag = (!isatty(0) || !isatty(1));/* flag for stdio redirected */ debug(F101,"ttopen fdflag","",fdflag); ttmdm = modem; /* Make this available to other fns */ xlocal = *lcl; /* Make this available to other fns */ /* Code for handling bidirectional tty lines goes here. /* Use specified method for turning off logins and suppressing getty. */ #ifdef ACUCNTRL /* Should put call to priv_on() here, but that would be very risky! */ acucntrl("disable",ttname); /* acucntrl() program. */ /* and priv_off() here... */ #else #ifdef ATT7300 if ((attmodem & DOGETY) == 0) /* offgetty() program. */ attmodem |= offgetty(ttname); /* Remember response. */ #endif /* ATT7300 */ #endif /* ACUCNTRL */ #ifdef datageneral /* A custom open is done for the device, but a FILE handle is also needed * for doing other special operations on the device. See sysinit() for * the initialization of the x_io_parms and xout_parms structures. * * First open up the output channel ($OFOT) */ idel_tbl[0] = 0x4; /* CR only */ xout_parms.idel = idel_tbl; xout_parms.ifnp = ttname; /* ttotmo = 0; saval = signal(SIGALRM,timerh); if (timo > 0) { alarm(timo); if (setjmp(sjbuf)) { } else { */ /* Ignore timeout for now - let it take as long as it wants */ retry: if ((ttfileout = dg_open(ttname, xout_parms.isti, xout_parms.isto, xout_parms.imrs,xout_parms.ires,xout_parms.idel)) == NULL) { /* If a modem disconnect, or one in progress, wait and retry. */ if ((lasterror() == ERDCT) || (lasterror() == ERCDS)) if (retrycnt == 0) { retrycnt = 1; sleep(2); goto retry; } ttyfdout = -1; perror("ttopen: output_line: dg_open"); if ((lasterror() == EREO1) || (lasterror() == EREO2)) /* in use */ return(-5); /* EOF */ else if (lasterror() == ERFAD) return(-4); /* access denied */ else /* some other error */ return(-1); } /* Successfull opens fall through here! */ ttyfdout = fileno(ttfileout); /* Note that ttyfdout is */ xout_parms.ich = channel(ttyfdout); /* only used for asynch */ /* Next, open up the input channel ($OFIN) */ x_io_parms.ifnp = ttname; /* IDEL would default to NULL, FF, CR, NL as delimiters. * The only delimiters here are CR and NL, for data sensitive * records. */ idel_tbl[0] = 0x24; x_io_parms.idel = idel_tbl; if ((ttfile = dg_open(ttname, x_io_parms.isti, x_io_parms.isto, x_io_parms.imrs,x_io_parms.ires,x_io_parms.idel, (P_SCREEN *) x_io_parms.etsp)) == NULL) { ttyfd = -1; perror("ttopen: input_line: dg_open"); if ((lasterror() == EREO1) || (lasterror() == EREO2)) /* in use */ return(-5); /* EOF */ else if (lasterror() == ERFAD) return(-4); /* access denied */ else /* some other error */ return(-1); } else { ttyfd = fileno(ttfile); x_io_parms.ich = channel(ttyfd); } #endif /* Make sure it's a real tty. */ if (!isatty(ttyfd)) { fprintf(stderr,"%s is not a tty!\n",ttname); debug(F110,"ttopen not a tty",ttname,0); close(ttyfd); ttyfd = -1; return(-1); } strncpy(ttnmsv,ttname,DEVNAMLEN); /* Open, keep copy of name locally. */ /* Caller wants us to figure out if line is controlling tty */ debug(F111,"ttopen ok",ttname,*lcl); if (*lcl == -1) { if (strcmp(ttname,CTTNAM) == 0) { /* "/dev/tty" always remote */ debug(F110," Same as CTTNAM",ttname,0); xlocal = 0; } else if (isatty(0)) { /* Else, if stdin not redirected */ x = ttyname(0); /* then compare its device name */ strncpy(cname,x,DEVNAMLEN); /* (copy from internal static buf) */ debug(F110," ttyname(0)",x,0); x = ttyname(ttyfd); /* ...with real name of ttname. */ xlocal = (strncmp(x,cname,DEVNAMLEN) == 0) ? 0 : 1; debug(F111," ttyname",x,xlocal); } /* Else, if stdin redirected... */ } /* Now check if line is locked -- if so fail, else lock for ourselves */ lkf = 0; /* Check lock */ if (xlocal > 0) { if (ttlock(ttname) < 0) { fprintf(stderr,"Exclusive access to %s denied\n",ttname); close(ttyfd); ttyfd = -1; debug(F110," Access denied by lock",ttname,0); return(-3); } else lkf = 1; } /* Got the line, now set the desired value for local. */ if (*lcl != 0) *lcl = xlocal; /* Request exclusive access on systems that allow it. */ #ifdef TIOCEXCL if (ioctl(ttyfd,TIOCEXCL, NULL) < 0) fprintf(stderr,"Warning, problem getting exclusive access\n"); #endif /* Get tty device settings */ ioctl(ttyfd,TCGETA,&ttold); /* Same deal for Sys III, Sys V */ ioctl(ttyfd,TCGETA,&ttraw); ioctl(ttyfd,TCGETA,&tttvt); debug(F101,"ttopen, ttyfd","",ttyfd); debug(F101,"ttopen, ttyfdout","",ttyfdout); /* For DG, also get the tty extended device characteristics. */ sys_gechr( channel(ttyfd), (1<<31)|$CLMAX, ch_ttold); sys_gechr( channel(ttyfd), (1<<31)|$CLMAX, ch_ttraw); sys_gechr( channel(ttyfd), (1<<31)|$CLMAX, ch_ttvt); debug(F101," lcl","",*lcl); debug(F111," lock file",flfnam,lkf); return(0); } /* T T C L O S -- Close the TTY, releasing any lock. */ int ttclos(foo) int foo; { int x = 0; debug(F101,"ttclos ttyfd","",ttyfd); if (ttyfd < 0) return(0); /* Wasn't open. */ if (ttfdflg) return(0); /* If we got ttyfd from another */ /* process, don't close it. */ tvtflg = 0; #ifdef NETCONN if (netconn) { /* Network connection. */ debug(F100,"ttclos closing net","",0); netclos(); /* Close it. */ netconn = 0; return(0); } #endif /* NETCONN */ if (xlocal) { debug(F100,"ttclos skipping ttunlck for AOS/VS","",0); debug(F100,"ttclos about to call tthang()","",0); } if (ttyfd > 0) { /* & timer stuff & moved closes in */ saval = signal(SIGALRM,timerh); /* enable timer */ alarm(5); /* allow 5 seconds for completion */ if (setjmp(sjbuf)) { /* timer went off */ x = -1; /* set return value */ } else { /* what we're really trying to do */ if (xlocal) { if (tthang()) /* Hang up first, then... */ fprintf(stderr,"Warning, problem hanging up the phone\n"); ttres(); /* reset device modes. */ } #ifdef TIOCEXCL #ifdef TIOCNXCL if (ioctl(ttyfd, TIOCNXCL, NULL) < 0) fprintf(stderr,"Problem relinquishing exclusive access\n"); #endif #endif debug(F101,"ttclos, ttyfd","",ttyfd); debug(F101,"ttclos, ttyfdout","",ttyfdout); fclose(ttfile); /* Close it. */ /* Close the output tty device */ fclose(ttfileout); /* Close it. */ x = 1; /* good return */ } ttimoff(); /* turn off timer */ if (x < 0) { fprintf(stderr,"?Timed out closing device: %s\n",ttnmsv); debug(F100,"ttclos timed out","",0); } } ttyfd = -1; /* Mark it as closed. */ ttyfdout = -1; /* Mark it as closed. */ debug(F100,"ttclos done","",0); return(0); } /* T T H A N G -- Hangup phone line */ tthang() { unsigned short ttc_save; int x; if (ttyfd < 0) return(0); /* Not open. */ if (xlocal < 1) return(0); #ifdef NETCONN if (netconn) { /* Network connection. */ return ((netclos() < 0) ? -1 : 1); /* Close it */ } #endif /* NETCONN */ ttc_save = ttraw.c_cflag; ttraw.c_cflag &= ~CBAUD; /* swa: set baud rate to 0 to hangup */ if (ioctl(ttyfd,TCSETAF,&ttraw) < 0) return(-1); /* do it */ msleep(100); /* let things settle */ ttraw.c_cflag = ttc_save; debug(F100, "tthang disconnecting line","",0); if (sys_sechr( channel(ttyfd), (1<<31)|$CLMAX, ch_ttraw)) { perror("tthang: sechr "); return(-1); } if (ioctl(ttyfd,TCSETAF,&ttraw) < 0) return(-1); /* un-do it */ return (0); } /* T T R E S -- Restore terminal to "normal" mode. */ ttres() { /* Restore the tty to normal. */ int x; if (ttyfd < 0) return(-1); /* Not open. */ if (ttfdflg) return(0); /* Don't mess with terminal modes if */ /* we got ttyfd from another process */ #ifdef NETCONN if (netconn) return (0); /* Network connection, do nothing */ #endif /* NETCONN */ x = (ioctl(ttyfd,TCSETAW,&ttold) < 0); /* restore termio stuff */ /* Un-set all the timeouts */ if (timotty) { resto(channel(ttyfd)); resto(channel(ttyfdout)); } if (sys_sechr( channel(ttyfd), (1<<31)|$CLMAX, ch_ttold)) { perror("ttres: sechr "); return(-1); } return(0); } /* Exclusive uucp file locking control */ /* by H. Fischer, creative non-Bell coding ! copyright rights for lock modules assigned to Columbia University */ /* The AOS/VS implementation does not use uucp locks. --ENH */ static char * xxlast(s,c) char *s; char c; { /* Equivalent to strrchr() */ int i; for (i = strlen(s); i > 0; i--) if ( s[i-1] == c ) return( s + (i - 1) ); return(NULL); } static look4lk(ttname) char *ttname; { extern char *strcat(), *strcpy(); char *device, *devname; char lockfil[50]; /* Max length for lock file name */ char *lockdir = "/usr/spool/uucp"; device = ( (devname=xxlast(ttname,'/')) != NULL ? devname+1 : ttname); strcat( strcpy( lockfil, "LCK.." ), device ); if (access( lockdir, 04 ) < 0) { /* read access denied on lock dir */ fprintf(stderr,"Warning, read access to lock directory denied\n"); return( 1 ); /* cannot check or set lock file */ } strcat(strcat(strcpy(flfnam,lockdir),"/"), lockfil); debug(F110,"look4lk",flfnam,0); if ( ! access( flfnam, 00 ) ) { /* print out lock file entry */ char lckcmd[40] ; strcat( strcpy(lckcmd, "ls -l ") , flfnam); system(lckcmd); if (access(flfnam,02) == 0) printf("(You may type \"! rm %s\" to remove this file)\n",flfnam); return( -1 ); } if ( access( lockdir, 02 ) < 0 ) { /* lock file cannot be written */ fprintf(stderr,"Warning, write access to lock directory denied\n"); return( 1 ); } return( 0 ); /* okay to go ahead and lock */ } /* T T L O C K */ static ttlock(ttyfd) char *ttyfd; { /* lock uucp if possible */ #ifdef datageneral /* The Data General locks a device when that device is opened. So * return that the device is already locked. * Besides, because of internal multi-tasking, locking the device by * the ?ASSIGN call or an $IEXO flag would cause problem for the task * id that did not open the device first. */ #else datageneral int lck_fil, l4l; int pid_buf = getpid(); /* pid to save in lock file */ haslock = 0; /* not locked yet */ l4l = look4lk(ttyfd); if (l4l < 0) return (-1); /* already locked */ if (l4l == 1) return (0); /* can't read/write lock directory */ lck_fil = creat(flfnam, 0444); /* create lock file ... */ if (lck_fil < 0) return (-1); /* create of lockfile failed */ /* creat leaves file handle open for writing -- hf */ write (lck_fil, &pid_buf, sizeof(pid_buf) ); /* uucp expects int in file */ close (lck_fil); #endif datageneral haslock = 1; /* now is locked */ return(0); } /* T T U N L O C K */ static ttunlck() { /* kill uucp lock if possible */ #ifdef datageneral /* The DG automatically unlocks the device when it is closed, or when * the process is terminated for any reason. Unlocking is not needed. * Also, see the note for ttlock(). */ return(0); #else if (haslock) return( unlink( flfnam ) ); #endif } /* T T P K T -- Condition the communication line for packets. */ /* or for modem dialing */ #define DIALING 4 /* flags (via flow) for modem handling */ #define CONNECT 5 /* If called with speed > -1, also set the speed. */ /* Returns 0 on success, -1 on failure. */ ttpkt(speed,xflow,parity) long speed; int xflow, parity; { int s2; int s = -1; int x; extern int flow; /* REAL flow-control setting */ if (ttyfd < 0) return(-1); /* Not open. */ debug(F101,"ttpkt parity","",parity); debug(F101,"ttpkt xflow","",xflow); debug(F101,"ttpkt speed","",(int) speed); ttprty = parity; /* Let other tt functions see these. */ ttpflg = 0; /* Parity not sensed yet */ ttpmsk = ttprty ? 0177 : 0377; /* Parity stripping mask */ ttspeed = speed; #ifdef NETCONN /* Nothing to do for telnet */ if (netconn) return (0); #endif /* NETCONN */ if (ttfdflg && !isatty(ttyfd)) return(0); if (xflow != FLO_DIAL && xflow != FLO_DIAX) ttflow = xflow; /* Now make this available too. */ if (xlocal) { s2 = (int) (speed / 10L); /* convert bps to cps */ s = ttsspd(s2); /* get associated speed */ if (s < 0) return(-1); /* carrctl() call here in UNIX code */ } #ifdef UXIII if (flow == FLO_XONX) ttraw.c_iflag |= (IXON|IXOFF); else if (flow == FLO_NONE) ttraw.c_iflag &= ~(IXON|IXOFF); else if (flow == FLO_KEEP) { ttraw.c_iflag &= ~(IXON|IXOFF); ttraw.c_iflag |= (ttold.c_iflag & (IXON|IXOFF)); } else if (flow == FLO_RTSC) { /* RTS/CTS Flow control... */ #ifdef RTSXOFF /* This is the preferred way, according to SVID R4 */ if (ioctl(ttyfd,TCGETX,&rctsx) > -1) { rctsx.x_hflag |= RTSXOFF | CTSXON; ioctl(ttyfd,TCSETX,&rctsx); /* Ignore errors for now. */ } #else #ifdef CRTSCTS /* Found this one in */ ttraw.c_iflag |= CRTSCTS; #endif /* CRTSCRS */ #endif /* RTSXOFF */ } if (flow == FLO_DTRC) { /* DTR/CD Flow control... */ #ifdef DTRXOFF /* This is straight out of SVID R3 */ if (ioctl(ttyfd,TCGETX,&rctsx) > -1) { rctsx.x_hflag |= DTRXOFF | CDXON; ioctl(ttyfd,TCSETX,&rctsx); /* Ignore errors. */ } #endif /* DTRXOFF */ } if (flow == DIALING) ttraw.c_cflag |= CLOCAL|HUPCL; if (flow == CONNECT) ttraw.c_cflag &= ~CLOCAL; ttraw.c_lflag &= ~(ICANON|ECHO); ttraw.c_lflag &= ~ISIG; ttraw.c_lflag |= NOFLSH; /* Don't flush */ ttraw.c_iflag |= (BRKINT|IGNPAR); ttraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|IUCLC|INPCK|ISTRIP|IXANY); ttraw.c_oflag &= ~OPOST; #ifdef datageneral ttraw.c_lflag &= ~CSETPM; /* No DG page mode */ #endif ttraw.c_cc[4] = 1; ttraw.c_cflag &= ~(CSIZE|PARENB); ttraw.c_cflag |= (CS8|CREAD); ttraw.c_cc[5] = 0; #ifdef VINTR /* Turn off interrupt character */ if (xlocal == 0) /* so ^C^C can break us out of */ ttraw.c_cc[VINTR] = 0; /* packet mode. */ #endif /* VINTR */ if (s > -1) { ttraw.c_cflag &= ~CBAUD; ttraw.c_cflag |= s; /* set speed */ } if (ioctl(ttyfd,TCSETAW,&ttraw) < 0) return(-1); /* set new modes . */ #ifdef datageneral if (flow == FLO_XONX) { dgsetflow(channel(ttyfdout)); dgsetflow(channel(ttyfd)); } if (flow == FLO_NONE) { resflow(channel(ttyfdout)); resflow(channel(ttyfd)); } /* if flow is FLO_KEEP, don't do anything */ if (sys_gechr( channel(ttyfd), (1<<31)|$CLMAX, ch_ttraw)) { perror("ttpkt: gechr: "); return(-1); } #endif if (flow == DIALING) { #ifdef datageneral #ifndef aosvs if (fcntl(ttyfd,F_SETFL, fcntl(ttyfd, F_GETFL, 0)) < 0 ) return(-1); #endif #else if (fcntl(ttyfd,F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NDELAY) < 0 ) return(-1); close( open(ttnmsv,2) ); /* magic to force mode change!!! */ #endif } #endif #ifdef COMMENT ttflui(); /* Flush any pending input */ #endif /* COMMENT */ tvtflg = 0; return(0); } /* T T V T -- Condition communication line for use as virtual terminal */ ttvt(speed,flow) int speed, flow; { int s, s2; debug(F101,"ttvt ttyfd","",ttyfd); debug(F101,"ttvt tvtflg","",tvtflg); debug(F101,"ttvt speed","",speed); if (ttyfd < 0) return(-1); /* Not open. */ #ifdef NETCONN if (netconn) { tvtflg = 1; /* Network connections */ return(0); /* require no special setup */ } #endif /* NETCONN */ if (tvtflg != 0 && speed == ttspeed && flow == ttflow && ttcarr == curcarr) return(0); /* Already been called. */ if (ttfdflg && !isatty(ttyfd)) return(0); if (xlocal) { s2 = (int) (speed / 10L); /* convert bps to cps */ s = ttsspd(s2); /* Check the speed */ /* ENH - UNIX has call to carrctl here */ } if (flow == FLO_XONX) tttvt.c_iflag |= (IXON|IXOFF); else if (flow == FLO_KEEP) { tttvt.c_iflag &= ~(IXON|IXOFF); /* Turn off Xon/Xoff flags */ tttvt.c_iflag |= (ttold.c_iflag & (IXON|IXOFF)); /* OR in old ones */ }else if (flow == FLO_NONE) tttvt.c_iflag &= ~(IXON|IXOFF); if (flow == DIALING) tttvt.c_cflag |= CLOCAL|HUPCL; if (flow == CONNECT) tttvt.c_cflag &= ~CLOCAL; tttvt.c_lflag &= ~(ISIG|ICANON|ECHO); tttvt.c_iflag |= (IGNBRK|IGNPAR); tttvt.c_iflag &= ~(INLCR|IGNCR|ICRNL|IUCLC|BRKINT|INPCK|ISTRIP|IXANY); tttvt.c_oflag &= ~OPOST; tttvt.c_cflag &= ~(CSIZE|PARENB); tttvt.c_cflag |= (CS8|CREAD); tttvt.c_cc[4] = 1; tttvt.c_cc[5] = 0; if (s > -1) { tttvt.c_cflag &= ~CBAUD; tttvt.c_cflag |= s; /* set speed */ } if (ioctl(ttyfd,TCSETAW,&tttvt) < 0) return(-1); /* set new modes . */ #ifdef datageneral if (flow == FLO_XONX) { dgsetflow(channel(ttyfdout)); dgsetflow(channel(ttyfd)); } if (flow == FLO_NONE) { resflow(channel(ttyfdout)); resflow(channel(ttyfd)); } if (sys_gechr( channel(ttyfd), (1<<31)|$CLMAX, ch_ttvt)) { perror("ttvt: gechr: "); return(-1); } #endif if (flow == DIALING) { #ifdef datageneral #ifndef aosvs if (fcntl(ttyfd,F_SETFL, fcntl(ttyfd, F_GETFL, 0)) < 0 ) return(-1); #endif #else if (fcntl(ttyfd,F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NDELAY) < 0 ) return(-1); close( open(ttnmsv,2) ); /* magic to force mode change!!! */ #endif } ttspeed = speed; /* Done, remember how we were */ ttflow = flow; /* called, so we can decide how to */ tvtflg = 1; /* respond next time. */ debug(F101,"ttvt done","",tvtflg); return(0); } /* T T S S P D -- Return the internal baud rate code for 'speed'. */ ttsspd(cps) { int s,s2,spdok; debug(F101,"ttsspd","",cps); #ifdef NETCONN if (netconn) return (0); #endif /* NETCONN */ if (cps < 0) return(-1); spdok = 1; /* Assume arg ok */ switch (cps) { case 0: s = B0; break; /* Just the common ones. */ case 11: s = B110; break; /* The others from ttydev.h */ case 15: s = B150; break; /* could also be included if */ case 30: s = B300; break; /* necessary... */ case 60: s = B600; break; case 120: s = B1200; break; case 180: s = B1800; break; case 240: s = B2400; break; case 480: s = B4800; break; case 960: s = B9600; break; #ifdef datageneral /* ioctl() allows other speeds */ case 5: s = B50; break; case 7: s = B75; break; case 13: s = B134; break; case 360: s = B3600; break; case 720: s = B7200; break; case 1920: s = B19000; break; /* Some speeds are supported by the hardware thru PMGR. We don't bother about 45.5 but it could be added. */ case 38400: if (setspeed($CR38K)) spdok = 0; s = 0; break; #endif default: spdok = 0; fprintf(stderr,"Unsupported line speed - %d\n",cps * 10); fprintf(stderr,"Current speed not changed\n"); break; } if (spdok) return(s); else return(-1); } /* 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. */ int ttscarr(carrier) int carrier; { ttcarr = carrier; debug(F101, "ttscarr","",ttcarr); return(ttcarr); } int ttgmdm() { /* Read modem signals */ return(-3); /* ENH - return value of -3 means */ } /* not implemented, which it's not */ /* because this can't (easily) be */ /* done in AOS/VS */ int ttwmdm(mdmsig,timo) int mdmsig,timo; { /* wait timo seconds for specified */ return(-3); /* modem signals to appear */ } long ttgspd() { /* Get line speed */ P_CHAR_EX cp; /* characteristics packet */ int ac0,ac1,ac2,error; static int baud_rate[] = { 50,75,110,134,150,300,600,1200,1800,2000,2400,3600,4800, 7200,9600,19200}, baud_rate_x[] = {45,38400}; #ifdef NETCONN if (netconn) return (-1); #endif /* NETCONN */ if (ttyfd < 0) return (-1); else { ac0 = channel(ttyfd); ac1 = $CLMAX | BIT0; ac2 = (P_CHAR_EX *) &cp; error = sys($GECHR,&ac0,&ac1,&ac2); if (error) return(-1); if (cp.char_br0bit == 0) return(baud_rate[cp.char_br_1_4]); else return(baud_rate_x[cp.char_br_1_4]); } } int psuspend() { /* ENH - not possible in VS */ return(-1); } /* Interrupt Functions */ /* Set up terminal interrupts on console terminal */ #ifdef UXIII esctrp() { /* trap console escapes (^\) */ conesc = 1; signal(SIGQUIT,SIG_IGN); /* ignore until trapped */ } #endif /* C O N I N T -- Console Interrupt setter */ VOID conint(f,s) SIGTYP (*f)(), (*s)(); { /* Set an interrupt trap. */ if (!isatty(0)) { /* only for real ttys */ debug(F101,"conint not a tty","",isatty(0)); return; } if (backgrd) { /* must ignore signals in bkgrd */ debug(F101,"conint backgrd ignoring signals","",backgrd); return; } else debug(F101,"conint foreground catching signals","",0); /* Except for special cases below, ignore keyboard quit signal. ^\ too easily confused with connect escape, and besides, we don't want to leave lock files around. (Frank Prindle) */ #ifdef datageneral /* Set up the asynchronous task to read the console. * But, I don't want them set up all the time, so I will kludge up the * call in chkint() (ckuus3.c) to see if this asynch task is running: * conint_mt() */ #endif signal(SIGQUIT,SIG_IGN); #ifdef UXIII signal(SIGQUIT,esctrp); /* console escape in pkt modes */ if (conesc) { /* clear out pending escapes */ conesc = 0; } #endif if (conif) return; /* Nothing to do if already on. */ /* check if invoked in background -- if so signals set to be ignored */ if (signal(SIGINT,SIG_IGN) == SIG_IGN) { backgrd = 1; /* means running in background */ #ifdef UXIII signal(SIGQUIT,SIG_IGN); /* must leave signals ignored */ #endif return; } signal(SIGINT,f); /* Function to trap to on interrupt. */ signal(SIGHUP,f); /* Or hangup, so lock file cleared. */ conif = 1; /* Flag console interrupts on. */ } /* C O N N O I -- Reset console terminal interrupts */ connoi() { /* Console-no-interrupts */ if (!isatty(0)) return(0); /* only for real ttys */ if (backgrd) return; /* Ignore signals in background */ #ifdef datageneral /* Terminate the asynchronous task that reads the console for * interrupt characters. * But, I don't want to kill this task all the time, so I will kludge * up the call in input() (ckcfn2.c) to kill the asynch task if it * is running: connoi_mt() */ #endif signal(SIGINT,SIG_DFL); signal(SIGHUP,SIG_DFL); signal(SIGQUIT,SIG_DFL); conif = 0; /* Flag interrupt trapping off */ } #ifdef datageneral /* C O N I N T _ M T -- Asynch read console task */ void conint_mt() { int count = 0; /* Any input waiting? */ con_reads_mt = 1; /* Flag that task is active */ conint_avl = 0; /* Flag that interrupt gotten */ while (con_reads_mt) if ((conint_ch = coninc(2)) > 0) { /* If character at keyboard */ conint_avl = 1; /* we set the flag */ while ((conint_avl) && (++count <= 5)) sleep(1); /* & wait for char to be processed */ } count = 0; /* initialize counter */ conint_avl = 0; } /* C O N N O I _ M T -- Kill asynch read console task */ connoi_mt() { conint_avl = 0; if (con_reads_mt) { con_reads_mt = 0; midkill(CONINT_TSK); } } /* C O N S T A _ M T -- Start the console asynch read task */ consta_mt() { if ((con_reads_mt == 0) /* Not already running */ && (mtask(conint_mt, CONINT_STACK, CONINT_TSK, CONINT_PRI) != 0)) perror("consta_mt: Can't initiate task:"); } #endif datageneral /* 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 #define PEEKTYPE long 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]) /* not used right now */ /* myunrd(ch) CHAR ch; { ungotn = ch; } */ /* 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. */ int myfillbuf() { int n; int retry_count = 5; /* max # of retries */ #ifdef NETCONN if (netconn) { n = read(ttyfd, mybuf, sizeof(mybuf)); if (n <= 0) debug(F101,"myfillbuf() got error reading from network, ","",n); return(n); } else { /* console read */ #endif /* NETCONN */ while (1) { n = dgncinb(ttyfd, mybuf, sizeof(mybuf)); if (n > 0) { return(n); } if ((n == -EROVR) || (n == -1) || (n == -2) || (n == 0)) { if (n == -EROVR) debug(F101,"myfillbuf() got data overrun ind., ","",-n); else if (n == -1) debug(F101,"myfillbuf() got EOF ind., ","",n); else if (n == -2) debug(F101,"myfillbuf() got device timeout ind., ","",n); else debug(F101,"myfillbuf() got zero characters, ","",n); if (--retry_count > 0) { debug(F101,"myfillbuf() tries remaining, ","",retry_count); sleep(1); /* keep trying on data over- */ continue; /* run, EOF (-1), timeout (-2) & 0 */ } } else if (n < -2) /* VS errors complimented */ debug(F101,"myfillbuf() got AOS/VS error ","",-n); if (n == -1) /* EOF is -1, but caller */ n = 0; /* wants it to be zero */ else n = -1; /* all others -1 */ return(n); } #ifdef NETCONN } #endif /* NETCONN */ } /* T T C H K -- Tell how many characters are waiting in tty input buffer */ ttchk() { return(in_chk(ttyfd)); } /* T T F L U I -- Flush tty input buffer */ ttflui() { int ffd; #ifndef UXIII long n; #endif #ifdef TTLEBUF ttpush = -1; /* Clear the peek-ahead char */ while (le_data && (le_inbuf() > 0)) { CHAR ch = '\0'; if (le_getchar(&ch) > 0) { debug(F101,"ttflui le_inbuf ch","",ch); } } #endif /* TTLEBUF */ my_count = 0; /* Initialize myread() stuff */ my_item = -1; #ifdef NETCONN if (netconn) return(netflui()); #endif /* NETCONN */ ffd = xlocal ? ttyfd : 0; /* If not local, use stdin */ debug(F101,"ttflui xlocal","",xlocal); debug(F101,"ttflui ttyfd","",ttyfd); debug(F101,"ttflui ffd","",ffd); if (ffd < 0) return(-1); /* Not open. */ #ifdef UXIII #ifdef datageneral /* Since $ESGT is turned on in the sys_read packets, there is never a need * to flush the input buffer -- the system automatically does it each time * a read is issued. */ #endif #endif return(0); } int ttfluo() { /* Flush output buffer - dummy */ return(0); /* POSIX is only implementation */ } /* that needs this */ /* 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; { int i = 0, c = -2, x = 0; #ifdef TTLEBUF if (ttpush >= 0) { buf[0] = ttpush; /* Put pushed char in buffer*/ ttpush = -1; /* Clear the push buffer */ if (ttchk() > 0) return(ttxin(n-1, &buf[1]) + 1); else return(1); } if (le_data) { while (le_inbuf() > 0) { if (le_getchar(&buf[i])) { i++; n--; } } if (ttchk() > 0) return(ttxin(n,&buf[i])+i); else return(i); } #endif /* TTLEBUF */ ttpmsk = (ttprty) ? 0177: 0377; /* parity stripping mask */ debug(F101,"ttxin: n","",n); for( x = 0; (x > -1) && (x < n) && (c = myread()) >= 0; ) buf[x++] = c & ttpmsk; debug(F101," x","",x); for (c = 0; c < n; c++) buf[c] &= ttpmsk; if (x > 0) buf[x] = '\0'; else if (x < 0) x = -1; return(x); } #define TTOLMAXT 5 /* T T O L -- Similar to "ttinl", but for writing. */ ttol(s,n) int n; char *s; { int x,len,tries,outfd; #ifdef NETCONN if (netconn) { if (ttyfd < 0) return (-1); } else #endif /* NETCONN */ if (ttyfdout < 0) return (-1); debug(F101,"ttol n","",n); tries = TTOLMAXT; len = n; while ((n > 0) && (tries-- > 0)) { debug(F101,"ttol try","",TTOLMAXT - tries); #ifdef NETCONN if (netconn) x = write(ttyfd,s,n); else #endif /* NETCONN */ x = dgncoub(ttyfdout,s,n); if (x == n) { debug(F101,"ttol ok","",x); return(len); } else if (x < 0) { debug(F101,"ttol failed","",lasterror()); return(-1); } else { debug(F101,"ttol partial","",x); s += x; /* update buffer address for next */ n -= x; /* update length for next read */ if (x > 0) msleep(100); } } return((n < 1) ? len : -1); } /* T T O C -- Output a character to the communication line */ /* This function should only be used for interactive, character-mode operations, like terminal connection, script execution, dialer i/o, where the overhead of the signals and alarms does not create a bottleneck. */ ttoc(c) char c; { int x,xx; #define TTOC_TMO 15 c &= 0xff; /* debug(F101,"ttoc","",(CHAR) c); */ #ifdef NETCONN if (netconn) { if (ttyfd < 0) return(-1); } else #endif /* NETCONN */ if (ttyfdout < 0) return(-1); /* Not open. */ saval = signal(SIGALRM,timerh); xx = alarm(TTOC_TMO); if (xx < 0) xx = 0; /* Save old alarm value. */ if (setjmp(sjbuf)) { ttimoff(); if (xx - TTOC_TMO > 0) alarm(xx - TTOC_TMO); /* Restore previous one */ #ifdef NETCONN if (!netconn) { #endif /* NETCONN */ /* debug(F100,"ttoc timeout","",0); */ if (ttflow == FLO_XONX) { debug(F100,"ttoc flow","",ttflow); /* issue clear device */ } #ifdef NETCONN } #endif /* NETCONN */ return(-1); } else { #ifdef NETCONN if (netconn) /* use ttyfd for tcp connections */ x = write(ttyfd,&c,1); else /* ttyfdout for local connections */ #endif /* NETCONN */ x = (dgncoub(ttyfdout,&c,1)); if (x != 1) { ttimoff(); /* If error, turn off timer, */ alarm(xx); debug(F101,"ttoc error x","",x); debug(F101,"ttoc last error","",lasterror()); return (-1); } } ttimoff(); alarm(xx); return (0); } /* 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: (a) the packet length field if the "turn" argument is zero, or (b) on the packet-end character (eol) if the "turn" argument is nonzero and returns the number of characters read upon success, or if "max" was exceeded or the timeout interval expired before (a) or (b), returns -1. 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. */ /* ENH -- assume PARSENSE */ #ifdef CTRLC #undef CTRLC #endif /* CTRLC */ #define CTRLC '\03' int ttinl(dest,max,timo,eol,start,turn) int max,timo,turn; CHAR *dest,eol,start; { int pktlen = -1; int j; CHAR ch; int lplen = 0; int havelen = 0; if (ttyfd < 0) return(-1); /* Not open. */ if (turn) debug(F101,"ttinl read until eol, turn","",turn); else debug(F101,"ttinl read packet-length characters, turn","",turn); debug(F101,"ttinl max","",max); debug(F101,"ttinl timo","",timo); *dest = '\0'; /* Clear destination buffer */ if (timo < 0) timo = 0; if (timo) { /* Don't time out if timo == 0 */ saval = signal(SIGALRM,timerh); /* Enable timer interrupt */ alarm(timo); /* Set it. */ } if (setjmp(sjbuf)) { /* Timer went off? */ debug(F100,"ttinl timout","",0); /* Get here on timeout. */ debug(F110," with",(char *) dest,0); ttimoff(); /* Turn off timer */ return(-1); /* and return error code. */ } else { int i, m, n; int ccn = 0; int flag = 0; debug(F000,"ttinl start","",start); flag = 0; /* Start of packet flag */ ttpmsk = m = (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","",eol); 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); /* Don't let EINTR break packets. */ if (n == -3 && errno == EINTR && i > 0) { debug(F101,"ttinl myread i","",i); continue; } break; } debug(F101,"ttinl char","", (n & ttpmsk)); /* Figure out what the length is supposed to be in case the packet has no terminator (as with Honeywell GCOS-8 Kermit). */ #ifndef xunchar #define xunchar(ch) (((ch) - 32 ) & 0xFF ) /* Character to number */ #endif /* xunchar */ if ((flag == 0) && ((n & 0x7f) == start)) flag = 1; if (flag) dest[i++] = n & ttpmsk; /* If we have not been instructed to wait for a turnaround character, we can go by the packet length field. If turn != 0, we must wait for the end of line (eol) character before returning. */ if (i == 2) { pktlen = xunchar(dest[1] & 0x7f); havelen = (pktlen > 1); debug(F101,"ttinl length","",pktlen); } else if (i == 5 && pktlen == 0) { lplen = xunchar(dest[4] & 0x7f); } else if (i == 6 && pktlen == 0) { pktlen = lplen * 95 + xunchar(dest[5] & 0x7f) + 5; havelen = 1; debug(F101,"ttinl length","",pktlen); } /* Check cancellation */ if (!xlocal && xfrcan && ((n & ttpmsk) == xfrchr)) { if (++ccn >= xfrnum) { /* If xfrnum in a row, bail out. */ if (timo) { /* Clear timer. */ ttimoff(); } fprintf(stderr,"^C...\r\n"); /* Echo Ctrl-C */ return(-2); } } else ccn = 0; /* Not ^C, so reset ^C counter, */ if (flag == 0) { debug(F101,"ttinl skipping","",n); continue; } /* Check for end of packet */ if (((n & 0x7f) == eol) || (!turn && havelen && (i > pktlen+1))) { if ((n & 0x7f) != eol) { debug(F101,"ttinl EOP length","",pktlen); debug(F101,"ttinl i","",i); } else debug(F101,"ttinl got eol","",eol); dest[i] = '\0'; /* Terminate the string, */ /* 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 (ttpflg++ == 0 && 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); } if (timo) { /* Turn off timer. */ ttimoff(); } debug(F111,"ttinl got", dest,i); return(i); } } /* end of while() */ 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 -1 or other negative myread error code. */ int ttinc(timo) int timo; { int n = 0; CHAR ch = 0; if (ttyfd < 0) /* Not open. */ return(-2); #ifdef TTLEBUF if (ttpush >= 0) { debug(F111,"ttinc","ttpush",ttpush); ch = ttpush; ttpush = -1; return(ch); } if (le_data) { if (le_getchar(&ch) > 0) { debug(F111,"ttinc le_getchar","ch",ch); return(ch); } } #endif /* TTLEBUF */ if (timo <= 0) { /* Untimed. */ /* comm line failure returns -1 thru myread, so no &= 0377 */ n = myread(); /* Wait for a character... */ /* debug(F101,"ttinc n","",n); */ return(n < 0 ? n : n & ttpmsk); } else { int xx; saval = signal(SIGALRM,timerh); /* Timed, set up timer. */ xx = alarm(timo); /* calls because I'm getting errors */ if (setjmp(sjbuf)) /* about these signal calls -- " the */ n = -1; /* destination of a non-local GOTO is*/ else { /* an inactive block" */ n = myread(); /* If managing own buffer... */ /* debug(F101,"ttinc myread","",n); */ } ttimoff(); #ifdef NETCONN if (netconn) { if (n == -2) { /* read() returns 0 */ netclos(); /* on network read failure */ netconn = 0; /* ENH - deleted errno = ENOTCON */ } } #endif /* NETCONN */ return( (n < 0) ? n : (n & ttpmsk) ); /* Return masked char or neg. */ } } /* T T S N D B -- Send a BREAK signal */ ttsndb() { int x; long n; char spd; /* In VS, the duration of the break */ /* is until next write() or charac- */ debug(F101,"ttsndb ttyfd","",ttyfd); /* teristics change */ if (ttyfd < 0) return(-1); /* Not open. */ #ifdef NETCONN if (netconn) /* Send network BREAK */ return(netbreak()); #endif /* NETCONN */ #ifdef UXIII #ifdef datageneral if (sys_clrdv(channel(ttyfdout),1<<31,$CDSBRK)) { /* Send a BREAK */ perror("Can't send BREAK"); return(-1); } return(0); #endif #endif } /* T T S N D L B -- Send a "long" BREAK signal */ ttsndlb() { /* In VS, they're long by default -- see ttsndb() */ return(ttsndb()); } /* M S L E E P -- Millisecond version of sleep(). */ /* Intended only for small intervals. For big ones, just use sleep(). */ msleep(m) int m; { #ifdef UXIII #define CLOCK_TICK 17 /* 1/60 sec */ extern long times(); long t1, t2, tarray[4]; int t3; if (m <= 0) return(0); if ((t1 = times(tarray)) < 0) return(-1); while (1) { if ((t2 = times(tarray)) < 0) return(-1); t3 = ((int)(t2 - t1)) * CLOCK_TICK; if (t3 > m) return(t3); } #endif } /* R T I M E R -- Reset elapsed time counter */ rtimer() { tcount = time( (long *) 0 ); } /* G T I M E R -- Get current value of elapsed time counter in seconds */ gtimer() { int x; x = (int) (time( (long *) 0 ) - tcount); debug(F101,"gtimer","",x); /* rtimer(); */ return( (x < 0) ? 0 : x ); } /* Z T I M E -- Return date/time string */ ztime(s) char **s; { #ifdef UXIII extern long time(); /* Sys III/V way to do it */ char *ctime(); long clock_storage; clock_storage = time( (long *) 0 ); *s = ctime( &clock_storage ); #endif } /* C O N G M -- Get console terminal modes. */ /* Saves current console mode, and establishes variables for switching between current (presumably normal) mode and other modes. */ congm() { if (backgrd || !isatty(0)) { /* only for real ttys */ cgmf = -1; return (-1); /* it used to return a zero for this case */ } ioctl(0,TCGETA,&ccold); ioctl(0,TCGETA,&cccbrk); ioctl(0,TCGETA,&ccraw); /* For DG, also get the tty extended device characteristics. */ sys_gechr( channel(0), (1<<31)|$CLMAX, ch_ccold); sys_gechr( channel(0), (1<<31)|$CLMAX, ch_ccraw); sys_gechr( channel(0), (1<<31)|$CLMAX, ch_ccbrk); cgmf = 1; /* Flag that we got them. */ } /* C O N C B -- Put console in cbreak mode. */ /* Returns 0 if ok, -1 if not */ concb(esc) char esc; { int x; debug(F101,"concb backgrd","",backgrd); if (!isatty(0)) return(0); /* only for real ttys */ debug(F100,"concb isatty","",0); if (backgrd) return(0); if (cgmf == 0) congm(); /* Get modes if necessary. */ escchr = esc; /* Make this available to other fns */ ckxech = 1; /* Program can echo characters */ #ifdef datageneral cccbrk.c_lflag |= CBREAK; #endif cccbrk.c_lflag &= ~(ICANON|ECHO); cccbrk.c_cc[0] = 003; /* interrupt char is control-c */ cccbrk.c_cc[1] = escchr; /* escape during packet modes */ cccbrk.c_cc[4] = 1; cccbrk.c_cc[5] = 1; x = ioctl(0,TCSETAW,&cccbrk); /* set new modes . */ #ifdef datageneral if (sys_gechr( channel(0), (1<<31)|$CLMAX, ch_ccbrk)) { perror("concb: gechr: "); return(-1); } #endif #ifndef NOSETBUF if (x > -1) setbuf(stdout,NULL); /* Make console unbuffered. */ #endif /* NOSETBUF */ return(x); } /* C O N B I N -- Put console in binary mode */ /* Returns 0 if ok, -1 if not */ conbin(esc) char esc; { int filedes,filedes2; if (!isatty(0)) return(0); /* only for real ttys */ if (backgrd) return; /* ... */ if (cgmf == 0) congm(); /* Get modes if necessary. */ escchr = esc; /* Make this available to other fns */ ckxech = 1; /* Program can echo characters */ ccraw.c_lflag &= ~(ISIG|ICANON|ECHO); ccraw.c_iflag |= (BRKINT|IGNPAR); ccraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|IUCLC|IXON|IXANY|IXOFF |INPCK|ISTRIP); ccraw.c_oflag &= ~OPOST; /*** Kermit used to put the console in 8-bit raw mode, but some users have *** pointed out that this should not be done, since some sites actually *** use terminals with parity settings on their Unix systems, and if we *** override the current settings and stop doing parity, then their terminals *** will display blotches for characters whose parity is wrong. Therefore, *** the following two lines are commented out (Larry Afrin, Clemson U): *** *** ccraw.c_cflag &= ~(PARENB|CSIZE); *** ccraw.c_cflag |= (CS8|CREAD); *** *** Sys III/V sites that have trouble with this can restore these lines. ***/ ccraw.c_cc[4] = 1; ccraw.c_cc[5] = 1; #ifdef datageneral if (ioctl(0,TCSETAW,&ccraw)) return(-1); /* set new modes . */ if (sys_gechr( channel(0), (1<<31)|$CLMAX, ch_ccraw)) { perror("conbin: gechr: "); return(-1); } return(0); #else return(ioctl(0,TCSETAW,&ccraw) ); /* set new modes . */ #endif } /* C O N R E S -- Restore the console terminal */ conres() { if (cgmf == 0) return(0); /* Don't do anything if modes */ if (!isatty(0)) return(0); /* only for real ttys */ if (backgrd) return; /* ... */ #ifdef datageneral if (timocon) /* Timeouts are set, so un-set them. */ resto(channel(0)); #endif ckxech = 0; /* System should echo chars */ #ifdef datageneral if (ioctl(0,TCSETAW,&ccold)) return(-1); /* set new modes . */ if (sys_sechr( channel(0), (1<<31)|$CLMAX, ch_ccold)) { perror("conres: sechr "); return(-1); } return(0); #else return(ioctl(0,TCSETAW,&ccold)); #endif } /* C O N O C -- Output a character to the console terminal */ conoc(c) char c; { write(1,&c,1); } /* C O N X O -- Write x characters to the console terminal */ conxo(x,s) char *s; int x; { write(1,s,x); } /* C O N O L -- Write a line to the console terminal */ conol(s) char *s; { int len; len = strlen(s); write(1,s,len); } /* C O N O L A -- Write an array of lines to the console terminal */ conola(s) char *s[]; { int i; for (i=0 ; *s[i] ; i++) conol(s[i]); } /* C O N O L L -- Output a string followed by CRLF */ conoll(s) char *s; { conol(s); write(1,"\r\n",2); } /* Input buffer checking */ #ifdef SELECT /* NOTE: This can be compiled and linked OK with SELECT defined but it doesn't work at all. Anybody who cares and knows how to fix it, feel free. The select() call always returns -1. */ #include <:usr:include:sys:vs_tcp_types.h> #include <:usr:include:sys:vs_tcp_time.h> #endif /* SELECT */ in_chk(fd) int fd; { int x, n = 0; #ifdef SELECT struct timeval tv; fd_set rfds; #endif /* SELECT */ #ifdef TTLEBUF ttpush = -1; /* Clear the peek-ahead char */ while (le_data && (le_inbuf() > 0)) { CHAR ch = '\0'; if (le_getchar(&ch) > 0) { debug(F101,"ttflui le_inbuf ch","",ch); } } #endif /* TTLEBUF */ if (fd < 0) return(0); #ifdef SELECT FD_ZERO(&rfds); /* Initialize them */ FD_SET(fd,&rfds); /* We want to look at this fd */ tv.tv_sec = tv.tv_usec = 0L; /* A 0-valued timeval structure */ n = select(FD_SETSIZE, &rfds, (fd_set *)0, (fd_set *)0, &tv); debug(F101,"in_chk select n","",n); #endif /* SELECT */ if (my_count > 0) n += my_count; debug(F101,"ttchk chars available, ","",n); return(n); } /* C O N C H K -- Return how many characters available at console */ conchk() { int x; long n; if (backgrd) /* There is no typeahead in batch */ return; return(in_chk(0)); } #ifdef datageneral /* D G N C O U B -- Output len characters to the file number filenum */ dgncoub(filenum,chs,len) int filenum, len; char *chs; { int ac2,err; if (len == 0) return(0); if (filenum == ttyfdout) { xout_parms.ibad = chs; xout_parms.ircl = len; ac2 = &xout_parms; } else { w_io_parms.ich = channel(filenum); w_io_parms.ibad = chs; w_io_parms.ircl = len; ac2 = &w_io_parms; } if ((err = sys_write(ac2)) == 0) return(((P_NIO_EX *) ac2)->irlr); /* ENH - change to return length */ if ( err != ERLTL && err != EREOF ) { perror("dgncoub: sys_write "); return(-err); /* ENH - change to return */ } /* negative of error code */ else return(((P_NIO_EX *) ac2)->irlr); } /* D G N C I N B -- System level read of len characters from file number fileno. The global DG i/o structures are used for this. Return codes: irlr if data read ok, where irlr=length of data -1 if EOF -2 if timeout occured -err if an AOS/VS error occured, where err is errno */ int dgncinb(filenum,chs,len) int filenum,len; char *chs; { int ac2, /* I/O parameter address block */ err, /* Error from sys_read */ irlr; /* Number of bytes read */ int rbe; /* Ring buffer empty on ESGT read */ if (filenum == ttyfd) { x_io_parms.ibad = chs; x_io_parms.ircl = len; ac2 = (P_NIO_EX *) &x_io_parms; err = sys_read(ac2); irlr = x_io_parms.irlr; if (err == 0) { return(irlr); } rbe = (x_io_parms.etsp == 0) ? 0 : (x_io_scrn.esfc & $ESBE); } else { r_io_parms.ich = channel(filenum); r_io_parms.ibad = chs; r_io_parms.ircl = len; ac2 = (P_NIO_EX *) &r_io_parms; err = sys_read(ac2); irlr = r_io_parms.irlr; if (err == 0) { return(irlr); } rbe = (r_io_parms.etsp == 0) ? 0 : (r_io_scrn.esfc & $ESBE); } if ((err != ERLTL) && (err != ERDTO) && (err != EREOF)) { /* NOT line-too-long, timeout, or EOF errors. Don't print error when it is a data overrun. */ if (err != EROVR) perror("dgncinb: sys_read error"); return(-err); } else { /* If the system read buffer was empty when a screen management packet was supplied, then there is a virtual EOF condition. So we return an EOF flag in that case (rbe != 0). */ if ((err == EREOF) || rbe) return(-1); /* EOF */ if (err == ERDTO) if (irlr <= 0) /* if no data, */ return (-2); /* return -2 */ return(irlr); /* else chars read */ } } #endif datageneral /* C O N I N C -- Get a character from the console */ coninc(timo) int timo; { int n = 0; char ch; if (timo <= 0) { /* no timeout specified */ while (1) { /* Binary input */ n = dgncinb(0,&ch,1); if (n == 0) continue; if (n > 0) return(ch & 0377); /* Return the char if read */ else { if (lasterror() == EINTR) if (conesc) { /* If by SIGQUIT, */ conesc = 0; /* the conesc variable is set, */ return(escchr); /* so return the escape character. */ } else continue; /* By other signal, try again. */ else if (n == -2) { /* won't happen unless ?STOM */ debug(F101,"coninc got timeout ","",n); /* has happened */ continue; } else if (n == -1) /* behind our back */ debug(F101,"coninc got eof indication","",n); else if (n < -2) /* -n = VS error code */ debug(F101,"coninc got AOS/VS error","",-n); return(-1); } } } saval = signal(SIGALRM,timerh); /* else timeout specified */ alarm(timo); if (setjmp(sjbuf)) n = -2; else n = dgncinb(0, &ch, 1); ttimoff(); /* Turn off timer */ if (n > 0) return(ch & 0377); else { debug(F101, "coninc(timo) n","",n); debug(F101, "coninc(timo) errno","",lasterror()); if ((lasterror() == EINTR) && conesc != 0) { conesc = 0; return(escchr); /* User entered escape character. */ } else if (n == -2) /* won't happen */ debug(F101, "coninc got DEVICE timeout","",n); else if (n == -1) debug(F101,"coninc got eof indication","",n); else if (n < -2) /* -n = VS error code */ debug(F101,"coninc got AOS/VS error","",-n); return(-1); } } /* C O N G K S -- Console Get Keyboard Scancode */ #ifndef congks /* This function needs to be filled in with the various system-dependent system calls used by SUNOS, NeXT OS, Xenix, Aviion, etc, to read a full keyboard scan code. For now, it's a dummy. */ int congks(timo) int timo; { return(coninc(timo)); } #endif /* congks */ /* These security routines are here, but they're not used. There's no */ /* need for them in the AOS/VS implementation. When they were here, */ /* they caused superuser to be turned off when kermit was started with */ /* it on. Seemed to cause problems instead of solve them. --ENH */ int priv_ini() /* if superu is on, turn off */ { int ac0,ac1,ac2,error; ac0 = ac1 = ac2 = 0; error = sys( $SUSER, &ac0, &ac1, &ac2 ); if( ac0 == -1 ) /* if it's on... */ priv_off(); /* turn it off */ return 0; } int priv_on() /* turn superu on */ { int ac0,ac1,ac2,error; ac0 = -1; /* turn it on */ ac1 = ac2 = 0; error = sys( $SUSER, &ac0, &ac1, &ac2 ); if( error != NULL ) error = -1; /* an error */ return error; } int priv_off() { int ac0,ac1,ac2; ac0 = 1; /* turn it off */ ac1 = ac2 = 0; return sys( $SUSER, &ac0, &ac1, &ac2 ); } int priv_can() { return priv_off(); } int priv_chk() { return priv_off(); } VOID ttimoff() { /* Turn off any timer interrupts */ alarm(0); if (saval) signal(SIGALRM,saval); else signal(SIGALRM,SIG_DFL); saval = NULL; } /* Similarly, this ctrlc_chk() routine is here, but it's not used. */ void ctrlc_chk(buffer,n) int *buffer; int n; { #nolist #include #list extern int local,what; int i; extern SIGTYP trap(); debug(F101,"Entering ctrlc_chk -- number of chars in buf","",n); debug(F101,"ctrlc_chk local =","",local); debug(F101,"ctrlc_chk what =","",what); if (what == W_CONNECT) /* ignore ^C's in connect mode */ return; if (what == W_NOTHING) /* don't know what's what here */ return; /* so ignore ^C in case data */ if ((!local) && ((what == W_SEND) || (what == W_RECV) || (what == W_REMO))) return; for (i = 0; i < n; i++) /* otherwise it's W_SEND, W_RECV */ /* or W_REMOTE in local mode or */ /* W_COMMAND in either mode */ if ((buffer[i] & 0177) == CTRLC) { debug (F101,"Got ^C...","",what); my_count = 0; /* clear out buffer */ trap(what); /* call signal handler */ } }