Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: Notesfiles $Revision: 1.7 $; site okstate Path: utzoo!watmath!clyde!burl!ulysses!mhuxr!ihnp4!okstate!authorplaceholder From: gregg@okstate.UUCP Newsgroups: net.sources Subject: C-KERMIT (Part 9 of 10) Message-ID: <5200023@okstate> Date: Fri, 8-Mar-85 02:51:00 EST Article-I.D.: okstate.5200023 Posted: Fri Mar 8 02:51:00 1985 Date-Received: Sun, 10-Mar-85 03:44:26 EST Lines: 1854 Nf-ID: #N:okstate:5200023:000:52896 Nf-From: okstate!gregg Mar 8 01:51:00 1985 echo x - ckxunx.c sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckxunx.c Xchar *ckxv = "Unix tty I/O, 4.2(016), 5 Mar 85"; X X/* C-Kermit interrupt, terminal control & i/o functions for Unix systems */ X X/* F. da Cruz, Columbia University Center for Computing Activities */ X X#ifdef BSD4 Xchar *ckxsys = " 4.2 BSD"; X#endif X X#ifdef PROVX1 Xchar *ckxsys = " Pro-3xx Venix 1.0"; X#endif X X/* Tower support contributed by John Bray, Auburn University */ X#ifdef TOWER1 Xchar *ckxsys = " NCR Tower 1632, OS 1.02"; X#endif X X/* Sys III/V, Xenix, PC/IX support by Herm Fischer, Litton Data Systems */ X#ifdef UXIII X#ifdef XENIX Xchar *ckxsys = " Xenix/286"; X#else X#ifdef PCIX Xchar *ckxsys = " PC/IX"; X#else X#ifdef ISIII Xchar *ckxsys = " Interactive Systems Corp System III"; X#else Xchar *ckxsys = " AT&T System III/System V"; X#endif X#endif X#endif X#endif X X/* X Note - KERLD is the Berkeley Unix Berknet line driver, modified to pass X through all 8 bits, and to allow an arbitrary break character to be set. X Don't define this symbol unless you have made this modification to your X 4.2BSD kernel! X*/ X#ifdef BSD4 X/* #define KERLD */ /* <-- note, commented out */ X#endif X X/* X Variables available to outside world: X X dftty -- Pointer to default tty name string, like "/dev/tty". X dfloc -- 0 if dftty is console, 1 if external line. X dfprty -- Default parity X dfflow -- Default flow control X ckxech -- Flag for who echoes console typein: X 1 - The program (system echo is turned off) X 0 - The system (or front end, or terminal). X functions that want to do their own echoing should check this flag X before doing so. X X flfnam -- Name of lock file, including its path, e.g., X "/usr/spool/uucp/LCK..cul0" or "/etc/locks/tty77" X hasLock -- Flag set if this kermit established a uucp lock. X inbufc -- number of tty line rawmode unread characters X (system III/V unixes) X backgrd -- Flag indicating program executing in background ( & on X end of shell command). Used to ignore INT and QUIT signals. X X Functions for assigned communication line (either external or console tty): X X ttopen(ttname,local,mdmtyp) -- Open the named tty for exclusive access. X ttclos() -- Close & reset the tty, releasing any access lock. X ttpkt(speed,flow) -- Put the tty in packet mode and set the speed. X ttvt(speed,flow) -- Put the tty in virtual terminal mode. X or in DIALING or CONNECTED modem control state. X ttinl(dest,max,timo) -- Timed read line from the tty. X ttinc(timo) -- Timed read character from tty. X myread() -- System 3 raw mode bulk buffer read, gives X -- subsequent chars one at a time and simulates X -- FIONREAD! X myunrd(c) -- Places c back in buffer to be read (one only) X ttchk() -- See how many characters in tty input buffer. X ttxin(n,buf) -- Read n characters from tty (untimed). X ttol(string,length) -- Write a string to the tty. X ttoc(c) -- Write a character to the tty. X ttflui() -- Flush tty input buffer. X X ttlock(ttname) -- Lock against uucp collisions (Sys III) X ttunlck() -- Unlock " " " X look4lk(ttname) -- Check if a lock file exists X*/ X X/* XFunctions for console terminal: X X congm() -- Get console terminal modes. X concb(esc) -- Put the console in single-character wakeup mode with no echo. X conbin(esc) -- Put the console in binary (raw) mode. X conres() -- Restore the console to mode obtained by congm(). X conoc(c) -- Unbuffered output, one character to console. X conol(s) -- Unbuffered output, null-terminated string to the console. X conola(s) -- Unbuffered output, array of strings to the console. X conxo(n,s) -- Unbuffered output, n characters to the console. X conchk() -- Check if characters available at console (bsd 4.2). X Check if escape char (^\) typed at console (System III/V). X coninc(timo) -- Timed get a character from the console. X conint() -- Enable terminal interrupts on the console if not background. X connoi() -- Disable terminal interrupts on the console if not background. X XTime functions X X msleep(m) -- Millisecond sleep X ztime(&s) -- Return pointer to date/time string X*/ X X/* Includes */ X X#include/* Unix Standard i/o */ X#include /* Interrupts */ X#include /* Longjumps */ X X#ifdef UXIII X#include X#endif X X#ifndef PROVX1 X#include /* File information */ X#endif X X#ifndef DIRSIZ X#ifdef MAXNAMLEN X#define DIRSIZ MAXNAMLEN X#else X#define DIRSIZ 14 X#endif X#endif X X#ifdef UXIII X#include X#include X#include /* directory reading for locking */ X#include X#include /* error numbers for system returns */ Xextern int errno; /* system call error return */ X#endif X X#ifndef UXIII X#include /* Set/Get tty modes */ X#ifndef PROVX1 X#include /* Clock info (for break generation) */ X#endif X#endif X X#ifdef TOWER1 X#include /* Clock info for NCR Tower */ X#endif X X#include "ckdebu.h" /* Formats for debug() */ X X/* Declarations */ X X/* dftty is the device name of the default device for file transfer */ X/* dfloc is 0 if dftty is the user's console terminal, 1 if an external line */ X X#ifdef PROVX1 X char *dftty = "/dev/com1"; X int dfloc = 1; X#else X char *dftty = "/dev/tty"; X int dfloc = 0; X#endif X X int dfprty = 0; /* Parity (0 = none) */ X int dfflow = 1; /* Xon/Xoff flow control */ X int backgrd = 0; /* Assume in foreground (no '&' ) */ X Xint ckxech = 0; /* 0 if system normally echoes console characters, else 1 */ X X/* Declarations of variables global within this module */ X Xstatic jmp_buf sjbuf; /* Longjump buffer */ Xstatic int lkf = 0, /* Line lock flag */ X conif = 0, /* Console interrupts on/off flag */ X cgmf = 0, /* Flag that console modes saved */ X xlocal = 0, /* Flag for tty local or remote */ X ttyfd = -1; /* TTY file descriptor */ Xstatic char escchr; /* Escape or attn character */ X X#ifdef KERLD X static int kerld = 1; /* Special Kermit line discipline... */ X struct tchars oldc, newc; /* Special characters */ X int ld = NETLDISC; /* Special Kermit line discipline */ X int oldld; /* Old discipline */ X#else X static int kerld = 0; /* for 4.2BSD only, */ X#endif X X#ifdef BSD4 X static struct timeval tv; /* For getting time, from sys/time.h */ X static struct timezone tz; X#endif X X#ifdef TOWER1 Xstatic long clock; /* For getting time from sys/time.h */ Xstatic struct timeb ftp; /* And from sys/timeb.h */ X#endif X X#ifdef UXIII X static struct termio /* sgtty info... */ X ttold, ttraw, tttvt, /* for communication line */ X ccold, ccraw, cccbrk; /* and for console */ X#else X static struct sgttyb /* sgtty info... */ X ttold, ttraw, tttvt, /* for communication line */ X ccold, ccraw, cccbrk; /* and for console */ X#endif X Xstatic char flfnam[80]; /* uucp lock file path name */ Xstatic int hasLock = 0; /* =1 if this kermit locked uucp */ Xstatic int inbufc = 0; /* stuff for efficient SIII raw line */ Xstatic int ungotn = -1; /* pushback to unread character */ Xstatic int conesc = 0; /* set to 1 if esc char (^\) typed */ X Xstatic int ttlock(); /* definition of ttlock subprocedure */ Xstatic int ttunlck(); /* unlock subprocedure */ X X/* T T O P E N -- Open a tty for exclusive access. */ X X/* Returns 0 on success, -1 on failure. */ X Xttopen(ttname,lcl,modem) char *ttname; int lcl, modem; { X X if (ttyfd > -1) return(0); /* If already open, ignore this call */ X xlocal = lcl; /* Make available to other functions */ X#ifndef UXIII X ttyfd = open(ttname,2); /* Open a tty for read/write */ X#else X /* if modem connection, don't wait for carrier */ X ttyfd = open(ttname,O_RDWR | (modem ? O_NDELAY : 0) ); X#endif X if (ttyfd < 0) return(-1); X lkf = 0; X X#ifndef PROVX1 X if (xlocal) { X#ifdef BSD4 X if ((flock(ttyfd,(LOCK_EX|LOCK_NB)) < 0) || (ttlock(ttname) < 0 )) { X#else X#ifdef TOWER1 X if (ioctl(ttyfd,TIOCEXCL, NULL) < 0) { X#else X if (ttlock(ttname) < 0) { X#endif X#endif X fprintf(stderr,"Sorry - Exclusive access to %s was denied\n",ttname); X return(-1); /* Can't open if already locked */ X } else lkf = 1; X } X#endif X X#ifndef UXIII X gtty(ttyfd,&ttold); /* Get sgtty info */ X gtty(ttyfd,&ttraw); /* And a copy of it for packets*/ X gtty(ttyfd,&tttvt); /* And one for virtual tty service */ X#else X ioctl(ttyfd,TCGETA,&ttold); /* Same deal for Sys III, Sys V */ X ioctl(ttyfd,TCGETA,&ttraw); X ioctl(ttyfd,TCGETA,&tttvt); X#endif X debug(F101,"ttopen, ttyfd","",ttyfd); X debug(F111," lock file",flfnam,lkf); X return(0); X} X X/* T T C L O S -- Close the TTY, releasing any lock. */ X Xttclos() { X if (ttyfd < 0) return(0); /* Wasn't open. */ X#ifndef PROVX1 X#ifdef BSD4 X if (lkf) flock(ttyfd,LOCK_UN); /* Unlock it first. */ X#endif X if (xlocal) ttunlck(); X#endif X ttres(); /* Reset modes. */ X close(ttyfd); /* Close it. */ X ttyfd = -1; /* Mark it as closed. */ X return(0); X} X X X/* T T R E S -- Restore terminal to "normal" mode. */ X Xttres() { /* Restore the tty to normal. */ X if (ttyfd < 0) return(-1); /* Not open. */ X sleep(1); /* Wait for pending i/o to finish. */ X#ifdef KERLD X if (kerld) ioctl(ttyfd,TIOCSETD,&oldld); /* Restore old line discipline. */ X#endif X#ifdef UXIII X if (ioctl(ttyfd,TCSETA,&ttold) < 0) return(-1); /* restore termio stuff */ X#else X debug(F101,"ttres, ttyfd","",ttyfd); X if (stty(ttyfd,&ttold) < 0) return(-1); /* Restore sgtty stuff */ X#endif X#ifdef KERLD X if (kerld) ioctl(ttyfd,TIOCSETC,&oldc); /* Restore old special chars. */ X#endif X X return(0); X} X X#ifndef PROVX1 X X/* Exclusive uucp file locking control */ X/* X by H. Fischer, creative non-Bell coding ! X*/ Xstatic char * Xxxlast(s,c) char *s; char c; { /* Equivalent to strrchr() */ X int i; X for (i = strlen(s); i > 0; i--) X if ( s[i-1] == c ) return( s + (i - 1) ); X return(NULL); X} Xstatic Xlook4lk(ttname) char *ttname; { X extern char *strcat(), *strcpy(); X char *device, *devname; X char lockfil[DIRSIZ+1]; X X#ifdef ISIII X char *lockdir = "/etc/locks"; X#else X char *lockdir = "/usr/spool/uucp"; X#endif X X device = ( (devname=xxlast(ttname,'/')) != NULL ? devname+1 : ttname); X X#ifdef ISIII X (void) strcpy( lockfil, device ); X#else X (void) strcat( strcpy( lockfil, "LCK.." ), device ); X#endif X X if (access( lockdir, 04 ) < 0) { /* read access denied on lock dir */ X fprintf(stderr,"Warning, read access to lock directory denied\n"); X return( 1 ); /* cannot check or set lock file */ X } X X (void) strcat(strcat(strcpy(flfnam,lockdir),"/"), lockfil); X debug(F110,"look4lk",flfnam,0); X X if ( ! access( flfnam, 00 ) ) { /* print out lock file entry */ X char lckcmd[40] ; X (void) strcat( strcpy(lckcmd, "ls -l ") , flfnam); X system(lckcmd); X return( -1 ); X } X if ( access( lockdir, 02 ) < 0 ) { /* lock file cannot be written */ X fprintf(stderr,"Warning, write access to lock directory denied\n"); X return( 1 ); X } X return( 0 ); /* okay to go ahead and lock */ X} X X/* T T L O C K */ X Xstatic Xttlock(ttyfd) char *ttyfd; { /* lock uucp if possible */ X int lck_fil, l4l; X X hasLock = 0; /* not locked yet */ X if ((l4l=look4lk(ttyfd)) < 0) return(-1); /* already locked */ X if (l4l == 1) return (0); /* can't read/write lock directory */ X if ((lck_fil=open (flfnam, O_CREAT | O_EXCL)) < 1 ) X return(-1); /* failed to create */ X close (lck_fil); X hasLock = 1; /* now is locked */ X return(0); X} X X/* T T U N L O C K */ X Xstatic Xttunlck() { /* kill uucp lock if possible */ X if (hasLock) unlink( flfnam ); X} X#endif X X X X/* T T P K T -- Condition the communication line for packets. */ X/* or for modem dialing */ X X#define DIALING 4 /* flags (via flow) for modem handling */ X#define CONNECT 5 X X/* If called with speed > -1, also set the speed. */ X X/* Returns 0 on success, -1 on failure. */ X Xttpkt(speed,flow) int speed, flow; { X extern char ttname[]; X int s; X if (ttyfd < 0) return(-1); /* Not open. */ X X#ifdef KERLD X if (kerld) { X ioctl(ttyfd,TIOCGETD,&oldld); /* Get line discipline */ X ioctl(ttyfd,TIOCGETC,&oldc); /* Get special chars */ X newc = oldc; /* Copy special chars */ X newc.t_brkc = '\r'; /* Set CR to be break character */ X if(ioctl(ttyfd,TIOCSETC,&newc) < 0) return(-1); X } X#endif X X/* cont'd... */ X X/* ...ttpkt(), cont'd */ X X X/* Note, KERLD ignores the TANDEM, ECHO, and CRMOD bits */ X X s = ttsspd(speed); /* Check the speed */ X X#ifndef UXIII X if (flow == 1) ttraw.sg_flags |= TANDEM; /* Use XON/XOFF if selected */ X if (flow == 0) ttraw.sg_flags &= ~TANDEM; X ttraw.sg_flags |= RAW; /* Go into raw mode */ X ttraw.sg_flags &= ~(ECHO|CRMOD); /* Use CR for break character */ X#ifdef TOWER1 X ttraw.sg_flags &= ~ANYP; /* Must tell Tower no parityr */ X#endif X if (s > -1) ttraw.sg_ispeed = ttraw.sg_ospeed = s; /* Do the speed */ X if (stty(ttyfd,&ttraw) < 0) return(-1); /* Set the new modes. */ X#endif X X#ifdef UXIII X if (flow == 1) ttraw.c_iflag |= (IXON|IXOFF); X if (flow == 0) ttraw.c_iflag &= ~(IXON|IXOFF); X X if (flow == DIALING) ttraw.c_cflag |= CLOCAL|HUPCL; X if (flow == CONNECT) ttraw.c_cflag &= ~CLOCAL; X X ttraw.c_lflag &= ~(ICANON|ECHO); X ttraw.c_lflag |= ISIG; /* do check for interrupt */ X ttraw.c_iflag |= (BRKINT|IGNPAR); X ttraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|IUCLC); X ttraw.c_oflag &= ~(ONLCR|OCRNL|ONLRET); X ttraw.c_cc[4] = 1; X ttraw.c_cc[5] = 0; X X if (s > -1) ttraw.c_cflag &= ~CBAUD, ttraw.c_cflag |= s; /* set speed */ X X if (ioctl(ttyfd,TCSETA,&ttraw) < 0) return(-1); /* set new modes . */ X if (flow == DIALING) { X if (fcntl(ttyfd,F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NDELAY) < 0 ) X return(-1); X close( open(ttname,2) ); /* magic to force file open!!! */ X } X#endif X X#ifdef KERLD X if (kerld) { X if (ioctl(ttyfd,TIOCSETD,&ld) < 0) X return(-1); /* Set line discpline. */ X } X#endif X X ttflui(); /* Flush any pending input */ X return(0); X} X X/* T T V T -- Condition communication line for use as virtual terminal */ X Xttvt(speed,flow) int speed, flow; { X extern char ttname[]; X int s; X if (ttyfd < 0) return(-1); /* Not open. */ X X s = ttsspd(speed); /* Check the speed */ X X#ifndef UXIII X if (flow == 1) tttvt.sg_flags |= TANDEM; /* XON/XOFF if selected */ X if (flow == 0) tttvt.sg_flags &= ~TANDEM; X tttvt.sg_flags |= RAW; /* Raw mode */ X#ifdef TOWER1 X tttvt.sg_flags &= ~(ECHO|ANYP); /* No echo or system III ??? parity */ X#else X tttvt.sg_flags &= ~ECHO; /* No echo */ X#endif X if (s > -1) tttvt.sg_ispeed = tttvt.sg_ospeed = s; /* Do the speed */ X X/* NOTE-- bsd code needs clocal and o_Ndelay stuff here */ X return(stty(ttyfd,&tttvt)); X#else X if (flow == 1) tttvt.c_iflag |= (IXON|IXOFF); X if (flow == 0) tttvt.c_iflag &= ~(IXON|IXOFF); X X if (flow == DIALING) tttvt.c_cflag |= CLOCAL|HUPCL; X if (flow == CONNECT) tttvt.c_cflag &= ~CLOCAL; X X tttvt.c_lflag &= ~(ISIG|ICANON|ECHO); X tttvt.c_iflag |= (IGNBRK|BRKINT|IGNPAR); X tttvt.c_iflag &= ~(INLCR|IGNCR|ICRNL|IUCLC); X tttvt.c_oflag &= ~(ONLCR|OCRNL|ONLRET); X tttvt.c_cc[4] = 1; X tttvt.c_cc[5] = 0; X X if (s > -1) tttvt.c_cflag &= ~CBAUD, tttvt.c_cflag |= s; /* set speed */ X X if (ioctl(ttyfd,TCSETA,&tttvt) < 0) return(-1); /* set new modes . */ X if (flow == DIALING) { X if (fcntl(ttyfd,F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NDELAY) < 0 ) X return(-1); X close( open(ttname,2) ); /* magic to force file open!!! */ X } X return(0); X#endif X} X X/* T T S S P D -- Return the internal baud rate code for 'speed'. */ X Xttsspd(speed) { X int s, spdok; X X if (speed < 0) return(-1); X spdok = 1; /* Assume arg ok */ X switch (speed) { X case 0: s = B0; break; /* Just the common ones. */ X case 110: s = B110; break; /* The others from ttydev.h */ X case 150: s = B150; break; /* could also be included if */ X case 300: s = B300; break; /* necessary... */ X case 600: s = B600; break; X case 1200: s = B1200; break; X case 1800: s = B1800; break; X case 2400: s = B2400; break; X case 4800: s = B4800; break; X case 9600: s = B9600; break; X default: X spdok = 0; X fprintf(stderr,"Unsupported line speed - %d\n",speed); X fprintf(stderr,"Current speed not changed\n"); X break; X } X if (spdok) return(s); else return(-1); X } X X X X/* T T F L U I -- Flush tty input buffer */ X Xttflui() { X X#ifndef PROVX1 X long n; X#endif X if (ttyfd < 0) return(-1); /* Not open. */ X X#ifdef BSD4 X#ifdef TOWER1 X n = FREAD; /* Specify read queue */ X if (ioctl(ttyfd,TIOCFLUSH,&n) < 0) perror("flush failed"); X#endif X#endif X X#ifdef UXIII X inbufc = 0; X ungotn = -1; X if (ioctl(ttyfd,TCFLSH,0) < 0) perror("flush failed"); X#endif X X return(0); X} X X/* Interrupt Functions */ X X X/* Timeout handler for communication line input functions */ X Xtimerh() { X longjmp(sjbuf,1); X} X X X/* Set up terminal interrupts on console terminal */ X X#ifdef UXIII Xesctrp() { /* trap console escapes (^\) */ X conesc = 1; X signal(SIGQUIT,SIG_IGN); /* ignore until trapped */ X} X#endif X X X/* C O N I N T -- Console Interrupt setter */ X Xconint(f) int (*f)(); { /* Set an interrupt trap. */ X X if (backgrd) return; /* must ignore signals in bkgrd */ X X#ifdef UXIII X signal(SIGQUIT,esctrp); /* console escape in pkt modes */ X if (conesc) { /* clear out pending escapes */ X conesc = 0; X } X#endif X X if (conif) return; /* Nothing to do if already on. */ X X/* check if invoked in background -- if so signals set to be ignored */ X X if (signal(SIGINT,SIG_IGN) == SIG_IGN) { X backgrd = 1; /* means running in background */ X#ifdef UXIII X signal(SIGQUIT,SIG_IGN); /* must leave signals ignored */ X#endif X return; X } X signal(SIGINT,f); /* Function to trap to. */ X conif = 1; /* Flag console interrupts on. */ X} X X X/* C O N N O I -- Reset console terminal interrupts */ X Xconnoi() { /* Console-no-interrupts */ X X if (backgrd) return; /* must ignore signals in bkgrd */ X X signal(SIGINT,SIG_DFL); X conif = 0; X} X X/* myread() -- System III raw read buffer to block input up */ X Xmyread() { /* return character or -1 if disconnected */ X X static int inbuf_item; X static CHAR inbuf[257]; X CHAR readit; X X if (ungotn >= 0) readit = ungotn; X else { X if (inbufc > 0) X readit = inbuf[++inbuf_item]; X else { X if ((inbufc=read(ttyfd,inbuf,256)) == 0 ) return(-1); X readit = inbuf[inbuf_item=0]; X } X inbufc--; X } X ungotn = -1; X return(readit ); X } X Xmyunrd(ch) CHAR ch; { /* push back up to one character */ X ungotn = ch; X} X X X/* T T C H K -- Tell how many characters are waiting in tty input buffer */ X Xttchk() { X#ifndef PROVX1 X int n, x; X#ifdef BSD4 X x = ioctl(ttyfd, FIONREAD, &n); X return((x < 0) ? 0 : n); X#else X return(inbufc + (ungotn >= 0) ); X#endif X#else X return(0); X#endif X} X X X/* T T X I N -- Get n characters from tty input buffer */ X Xttxin(n,buf) int n; char *buf; { X int x; X#ifndef UXIII X x = read(ttyfd,buf,n); X#else X for( x=0; x 0) buf[x] = '\0'; X return(x); X} X X/* T T O L -- Similar to "ttinl", but for writing. */ X Xttol(s,n) int n; char *s; { X int x; X if (ttyfd < 0) return(-1); /* Not open. */ X x = write(ttyfd,s,n); X debug(F111,"ttol",s,n); X if (x < 0) debug(F101,"ttol failed","",x); X return(x); X} X X X/* T T O C -- Output a character to the communication line */ X Xttoc(c) char c; { X if (ttyfd < 0) return(-1); /* Not open. */ X return(write(ttyfd,&c,1)); X} X X/* T T I N L -- Read a record (up to break character) from comm line. */ X/* X If no break character encountered within "max", return "max" characters, X with disposition of any remaining characters undefined. Otherwise, return X the characters that were read, including the break character, in "dest" and X the number of characters read as the value of function, or 0 upon end of X file, or -1 if an error occurred. Times out & returns error if not completed X within "timo" seconds. X*/ X Xttinl(dest,max,timo,eol) int max,timo; char *dest; { X int x, y, c; X if (ttyfd < 0) return(-1); /* Not open. */ X if (timo <= 0) { /* Untimed. */ X#ifndef UXIII X x = read(ttyfd,dest,max); /* Try to read. */ X#else X for (x = c = 0; (x < max) && (c != eol); x++) { X c = myread(); X dest[x] = c; X } X#endif X return(x); /* Return the count. */ X } X signal(SIGALRM,timerh); /* Timed, set up timeout action. */ X alarm(timo); /* Set the timer. */ X if (setjmp(sjbuf)) x = -1; /* Do this if timer went off. */ X else if (kerld) { X x = read(ttyfd,dest,max); /* Try to read. */ X } else { X for (x = c = y = 0; (x < max) && (c != eol); x++) { X#ifndef UXIII X while ( !(y = read(ttyfd,&c,1))) ; /* skip null reads */ X if (y < 0) return(y); X#else X c = myread(); X#endif X dest[x] = c; X } X x++; X } X alarm(0); /* Success, turn off timer, */ X signal(SIGALRM,SIG_DFL); /* and associated interrupt. */ X return(x); /* Return the count. */ X} X X/* T T I N C -- Read a character from the communication line */ X Xttinc(timo) int timo; { X int n; X CHAR ch; X X if (ttyfd < 0) return(-1); /* Not open. */ X if (timo <= 0) { /* Untimed. */ X#ifndef UXIII X while ( !(n = read(ttyfd,&ch,1)) ) ; /* Wait for a character. */ X return( (n > 0) ? (ch & 0377) : n ); X#else X /* comm line failure returns -1 thru myread, so don't &= 0377 */ X return( myread() ); X#endif X } X X signal(SIGALRM,timerh); /* Timed, set up timer. */ X alarm(timo); X if (setjmp(sjbuf)) n = -1; X else { X#ifndef UXIII X n = read(ttyfd,&ch,1); /* Read a character. */ X#else X ch = myread(); X n = 1; X#endif X } X alarm(0); /* Turn off timer, */ X signal(SIGALRM,SIG_DFL); /* and interrupt. */ X if (n > 0) return(ch & 0377); else return(n); /* Return char or -1. */ X} X X/* T T S N D B -- Send a BREAK signal */ X Xttsndb() { X int x; X X if (ttyfd < 0) return(-1); /* Not open. */ X X#ifdef PROVX1 X/*** insert code to set speed to 50 baud and send 2-3 nulls ***/ X return(0); X#else X#ifdef UXIII X if (ioctl(ttyfd,TCSBRK,(char *)0) < 0) { /* Turn on BREAK */ X conol("Can't send BREAK"); X return(-1); X } X return(0); X#else X#ifdef BSD4 X if (ioctl(ttyfd,TIOCSBRK,(char *)0) < 0) { /* Turn on BREAK */ X conol("Can't send BREAK"); X return(-1); X } X x = msleep(275); /* Sleep for so many milliseconds */ X if (ioctl(ttyfd,TIOCCBRK,(char *)0) < 0) { /* Turn off BREAK */ X conol("BREAK stuck!!!"); X doexit(1); /* Get out, closing the line. */ X /* with exit status = 1 */ X } X return(x); X#endif X#endif X#endif X} X X/* M S L E E P -- Millisecond version of sleep(). */ X X/* X Intended only for small intervals. For big ones, just use sleep(). X*/ X Xmsleep(m) int m; { X X#ifdef PROVX1 X sleep(1+m/1000.0); X return(0); X#endif X X#ifdef BSD4 X int t1, t3, t4; X if (gettimeofday(&tv, &tz) < 0) return(-1); /* Get current time. */ X t1 = tv.tv_sec; /* Seconds */ X Xif (0) { /* Old way (works) */ X while (1) { X gettimeofday(&tv, &tz); X t3 = tv.tv_sec - t1; X t4 = (tv.tv_usec + 1000000 * t3) / 1000; X if (t4 > m) return(t4); X } X} else { /* New way (also works) */ X tv.tv_sec = 0; X tv.tv_usec = m * 1000; X return(select( 0, (int *)0, (int *)0, (int *)0, &tv) ); X} X#endif X X#ifdef TOWER1 X if (ftime(&ftp) < 0) return(-1); /* Get current time. */ X t1 = ((ftp.time & 0xff) * 1000) + ftp.millitm; X while (1) { X ftime(&ftp); /* new time */ X t3 = (((ftp.time & 0xff) * 1000) + ftp.millitm) - t1; X if (t3 > m) return (t3); X } X#endif X} X X/* Z T I M E -- Return date/time string */ X Xztime(s) char **s; { X X#ifdef UXIII X extern long time(); /* Sys III/V way to do it */ X char *ctime(); X long clock_storage; X X clock_storage = time( (long *) 0 ); X *s = ctime( &clock_storage ); X#endif X X#ifdef PROVX1 X int utime[2]; /* Venix way */ X time(utime); X *s = ctime(utime); X#endif X X#ifdef BSD4 X char *asctime(); /* Berkeley way */ X struct tm *localtime(); X struct tm *tp; X X gettimeofday(&tv, &tz); X time(&tv.tv_sec); X tp = localtime(&tv.tv_sec); X *s = asctime(tp); X#endif X X#ifdef TOWER1 X char *asctime(); /* Tower way */ X struct tm *localtime(); X struct tm *tp; X X time(&clock); X tp = localtime(&clock); X *s = asctime(tp); X#endif X} X X/* C O N G M -- Get console terminal modes. */ X X/* X Saves current console mode, and establishes variables for switching between X current (presumably normal) mode and other modes. X*/ X Xcongm() { X#ifndef UXIII X gtty(0,&ccold); /* Structure for restoring */ X gtty(0,&cccbrk); /* For setting CBREAK mode */ X gtty(0,&ccraw); /* For setting RAW mode */ X#else X ioctl(0,TCGETA,&ccold); X ioctl(0,TCGETA,&cccbrk); X ioctl(0,TCGETA,&ccraw); X#endif X cgmf = 1; /* Flag that we got them. */ X} X X X/* C O N C B -- Put console in cbreak mode. */ X X/* Returns 0 if ok, -1 if not */ X Xconcb(esc) char esc; { X int x; X if (cgmf == 0) congm(); /* Get modes if necessary. */ X escchr = esc; /* Make this available to other fns */ X ckxech = 1; /* Program can echo characters */ X#ifndef UXIII X cccbrk.sg_flags |= CBREAK; /* Set to character wakeup, */ X cccbrk.sg_flags &= ~ECHO; /* no echo. */ X x = stty(0,&cccbrk); X#else X cccbrk.c_lflag &= ~(ICANON|ECHO); X cccbrk.c_cc[0] = 003; /* interrupt char is control-c */ X cccbrk.c_cc[1] = escchr; /* escape during packet modes */ X cccbrk.c_cc[4] = 1; X cccbrk.c_cc[5] = 1; X x = ioctl(0,TCSETA,&cccbrk); /* set new modes . */ X#endif X if (x > -1) setbuf(stdout,NULL); /* Make console unbuffered. */ X return(x); X} X X/* C O N B I N -- Put console in binary mode */ X X X/* Returns 0 if ok, -1 if not */ X Xconbin(esc) char esc; { X if (cgmf == 0) congm(); /* Get modes if necessary. */ X escchr = esc; /* Make this available to other fns */ X ckxech = 1; /* Program can echo characters */ X#ifndef UXIII X ccraw.sg_flags |= (RAW|TANDEM); /* Set rawmode, XON/XOFF */ X ccraw.sg_flags &= ~(ECHO|CRMOD); /* Set char wakeup, no echo */ X return(stty(0,&ccraw)); X#else X ccraw.c_lflag &= ~(ISIG|ICANON|ECHO); X ccraw.c_iflag |= (BRKINT|IGNPAR); X ccraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|IUCLC|IXON|IXANY|IXOFF); X ccraw.c_oflag &= ~(ONLCR|OCRNL|ONLRET); X ccraw.c_cc[4] = 1; X ccraw.c_cc[5] = 1; X return(ioctl(0,TCSETA,&ccraw) ); /* set new modes . */ X#endif X} X X X/* C O N R E S -- Restore the console terminal */ X Xconres() { X if (cgmf == 0) return(0); /* Don't do anything if modes */ X sleep(1); /* not known! */ X ckxech = 0; /* System should echo chars */ X#ifndef UXIII X return(stty(0,&ccold)); /* Restore controlling tty */ X#else X return(ioctl(0,TCSETA,&ccold)); X#endif X} X X/* C O N O C -- Output a character to the console terminal */ X Xconoc(c) char c; { X write(1,&c,1); X} X X/* C O N X O -- Write x characters to the console terminal */ X Xconxo(x,s) char *s; int x; { X write(1,s,x); X} X X/* C O N O L -- Write a line to the console terminal */ X Xconol(s) char *s; { X int len; X len = strlen(s); X write(1,s,len); X} X X/* C O N O L A -- Write an array of lines to the console terminal */ X Xconola(s) char *s[]; { X int i; X for (i=0 ; *s[i] ; i++) conol(s[i]); X} X X/* C O N O L L -- Output a string followed by CRLF */ X Xconoll(s) char *s; { X conol(s); X write(1,"\r\n",2); X} X X X/* C O N C H K -- Check if characters available at console */ X Xconchk() { X#ifdef PROVX1 X return(0); X#else X int n, x; X#ifndef UXIII X x = ioctl(0, FIONREAD, &n); X return((x < 0) ? 0 : n); X#else X if (conesc) { /* Escape typed */ X conesc = 0; X signal(SIGQUIT,esctrp); /* restore escape */ X return(1); X } X return(0); X#endif X#endif X} X X/* C O N I N C -- Get a character from the console */ X Xconinc(timo) int timo; { X int n = 0; char ch; X if (timo <= 0 ) { /* untimed */ X n = read(0, &ch, 1); /* Read a character. */ X ch &= 0377; X if (n > 0) return(ch); /* Return the char if read */ X else X#ifdef UXIII X if (n < 0 && errno == EINTR) /* if read was interrupted by QUIT */ X return(escchr); /* user entered escape character */ X else /* couldnt be ^c, sigint never returns */ X#endif X return(-1); /* Return the char, or -1. */ X } X signal(SIGALRM,timerh); /* timed, set up timer */ X alarm(timo); X if (setjmp(sjbuf)) n = -2; X else { X n = read(0, &ch, 1); X alarm(0); X signal(SIGALRM,SIG_DFL); /* stop timing, we got our character */ X ch &= 0377; X } X if (n > 0) return(ch); X else X#ifdef UXIII X if (n == -1 && errno == EINTR) /* If read interrupted by QUIT, */ X return(escchr); /* user entered escape character, */ X else /* can't be ^c, sigint never returns */ X#endif X return(-1); X} !FUNKY!STUFF! echo x - ckzunx.c sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckzunx.c Xchar *ckzv = "Unix file support, 4.1(015) 28 Feb 85"; X X/* C K Z B S D -- Kermit file system support for Unix systems */ X X/* F. da Cruz, Columbia University Center for Computing Activities */ X X/* Berkeley Unix Version 4.x */ X#ifdef BSD4 Xchar *ckzsys = " 4.x BSD"; X#endif X X/* DEC Professional-300 series with Venturcom Venix 1.0 */ X#ifdef PROVX1 Xchar *ckzsys = " DEC Pro-3xx/Venix 1.0"; X#endif X X/* NCR Tower support contributed by Kevin O'Kane, U. of Tennessee */ X/* Tower OS is like Sys III but with BSD features -- mostly follows BSD */ X#ifdef TOWER1 Xchar *ckxsys = " NCR Tower 1632, OS 1.02"; X#endif X X/* Sys III/V, Xenix, PC/IX,... support by Herm Fischer, Litton Data Systems */ X#ifdef UXIII X#ifdef XENIX Xchar *ckzsys = " Xenix/286"; X#else X#ifdef PCIX Xchar *ckzsys = " PC/IX"; X#else X#ifdef ISIII Xchar *ckzsys = " Interactive Systems Corp, System III"; X#else Xchar *ckzsys = " AT&T System III/System V"; X#endif X#endif X#endif X#endif X X/* Definitions of some Unix system commands */ X Xchar *DIRCMD = "ls -l "; /* For directory listing */ Xchar *DELCMD = "rm -f "; /* For file deletion */ Xchar *TYPCMD = "cat "; /* For typing a file */ X X#ifdef BSD4 Xchar *SPACMD = "pwd ; quota ; df ."; /* Space/quota of current directory */ X#else Xchar *SPACMD = "df "; X#endif X Xchar *SPACM2 = "df "; /* For space in specified directory */ X X#ifdef BSD4 Xchar *WHOCMD = "finger "; /* For seeing who's logged in */ X#else Xchar *WHOCMD = "who "; /* For seeing who's logged in */ X#endif X X/* X Functions (n is one of the predefined file numbers from ckermi.h): X X zopeni(n,name) -- Opens an existing file for input. X zopeno(n,name) -- Opens a new file for output. X zclose(n) -- Closes a file. X zchin(n) -- Gets the next character from an input file. X zsout(n,s) -- Write a null-terminated string to output file, buffered. X zsoutl(n,s) -- Like zsout, but appends a line terminator. X zsoutx(n,s,x) -- Write x characters to output file, unbuffered. X zchout(n,c) -- Add a character to an output file, unbuffered. X zchki(name) -- Check if named file exists and is readable, return size. X zchko(name) -- Check if named file can be created. X znewn(name,s) -- Make a new unique file name based on the given name. X zdelet(name) -- Delete the named file. X zxpand(string) -- Expands the given wildcard string into a list of files. X znext(string) -- Returns the next file from the list in "string". X zxcmd(cmd) -- Execute the command in a lower fork. X zclosf() -- Close input file associated with zxcmd()'s lower fork. X zrtol(n1,n2) -- Convert remote filename into local form. X zltor(n1,n2) -- Convert local filename into remote form. X zchdir(dirnam) -- Change working directory. X zhome() -- Return pointer to home directory name string. X */ X X/* Includes */ X X#include "ckermi.h" /* Kermit definitions, ctype, stdio */ X#include /* Data types */ X#include /* Directory structure */ X#include /* File status */ X#include /* Password file for shell name */ X X#ifndef PROVX1 X#include /* File access */ X#endif X X/* Some systems define these in include files, others don't... */ X X#ifndef R_OK X#define R_OK 4 /* For access */ X#endif X X#ifndef W_OK X#define W_OK 2 X#endif X X#ifdef PROVX1 X#define MAXNAMLEN DIRSIZ /* Max file name length */ X#endif X X#ifdef UXIII X#include X#define MAXNAMLEN DIRSIZ X#endif X X#ifndef O_RDONLY X#define O_RDONLY 000 X#endif X X#ifndef MAXNAMLEN X#define MAXNAMLEN 14 /* If still not defined... */ X#endif X X#define MAXWLD 500 /* Maximum wildcard filenames */ X X X/* Declarations */ X XFILE *fp[ZNFILS] = { /* File pointers */ X NULL, NULL, NULL, NULL, NULL, NULL, NULL }; X Xstatic int pid; /* pid of child fork */ Xstatic int fcount; /* Number of files in wild group */ Xchar *getenv(), *strcpy(); /* For finding home directory */ Xextern errno; /* System error code */ X Xstatic char *mtchs[MAXWLD], /* Matches found for filename */ X **mtchptr; /* Pointer to current match */ X X/* Z O P E N I -- Open an existing file for input. */ X Xzopeni(n,name) int n; char *name; { X debug(F111," zopeni",name,n); X debug(F101," fp","",(int) fp[n]); X if (chkfn(n) != 0) return(0); X if (n == ZSTDIO) { /* Standard input? */ X if (isatty(0)) { X fprintf(stderr,"?Terminal input not allowed\n"); X debug(F110,"zopeni: attempts input from unredirected stdin","",0); X return(0); X } X fp[ZIFILE] = stdin; X return(1); X } X fp[n] = fopen(name,"r"); /* Real file. */ X debug(F111," zopeni", name, (int) fp[n]); X if (fp[n] == NULL) perror("zopeni"); X return((fp[n] != NULL) ? 1 : 0); X} X X/* Z O P E N O -- Open a new file for output. */ X Xzopeno(n,name) int n; char *name; { X debug(F111," zopeno",name,n); X if (chkfn(n) != 0) return(0); X if ((n == ZCTERM) || (n == ZSTDIO)) { /* Terminal or standard output */ X fp[ZOFILE] = stdout; X debug(F101," fp[]=stdout", "", (int) fp[n]); X return(1); X } X fp[n] = fopen(name,"w"); /* A real file */ X if (fp[n] == NULL) perror("zopeno"); X if (n == ZDFILE) setbuf(fp[n],NULL); /* Make debugging file unbuffered */ X debug(F101, " fp[n]", "", (int) fp[n]); X return((fp[n] != NULL) ? 1 : 0); X} X X/* Z C L O S E -- Close the given file. */ X Xzclose(n) int n; { X if (chkfn(n) < 1) return(0); X if ((fp[n] != stdout) && (fp[n] != stdin)) fclose(fp[n]); X fp[n] = NULL; X return(1); X} X X/* Z C H I N -- Get a character from the input file. */ X Xzchin(n) int n; { X int a; X if (chkfn(n) < 1) return(-1); X a = getc(fp[n]); X return((a == EOF) ? -1 : a & 0377); X} X X/* Z S O U T -- Write a string to the given file, buffered. */ X Xzsout(n,s) int n; char *s; { X if (chkfn(n) < 1) return(-1); X fprintf(fp[n],s); X return(0); X} X X/* Z S O U T L -- Write string to file, with line terminator, buffered */ X Xzsoutl(n,s) int n; char *s; { X if (chkfn(n) < 1) return(-1); X fprintf(fp[n],"%s\n",s); X return(0); X} X X/* Z S O U T X -- Write x characters to file, unbuffered. */ X Xzsoutx(n,s,x) int n, x; char *s; { X if (chkfn(n) < 1) return(-1); X return(write(fp[n]->_file,s,x)); X} X X X/* Z C H O U T -- Add a character to the given file. */ X Xzchout(n,c) int n; char c; { X if (chkfn(n) < 1) return(-1); X if (n == ZSFILE) X return(write(fp[n]->_file,&c,1)); /* Use unbuffered for session log */ X else { X putc(c,fp[n]); /* Buffered for everything else */ X return(0); X } X} X X/* C H K F N -- Internal function to verify file number is ok */ X X/* X Returns: X -1: File number n is out of range X 0: n is in range, but file is not open X 1: n in range and file is open X*/ Xchkfn(n) int n; { X switch (n) { X case ZCTERM: X case ZSTDIO: X case ZIFILE: X case ZOFILE: X case ZDFILE: X case ZTFILE: X case ZPFILE: X case ZSFILE: break; X default: X debug(F101,"chkfn: file number out of range","",n); X fprintf(stderr,"?File number out of range - %d\n",n); X return(-1); X } X return( (fp[n] == NULL) ? 0 : 1 ); X} X X/* Z C H K I -- Check if input file exists and is readable */ X X/* X Returns: X >= 0 if the file can be read (returns the size). X -1 if file doesn't exist or can't be accessed, X -2 if file exists but is not readable (e.g. a directory file). X -3 if file exists but protected against read access. X*/ X/* X For Berkeley Unix, a file must be of type "regular" to be readable. X Directory files, special files, and symbolic links are not readable. X*/ Xzchki(name) char *name; { X struct stat buf; X int x; X X x = stat(name,&buf); X if (x < 0) { X debug(F111,"zchki stat fails",name,errno); X return(-1); X } X x = buf.st_mode & S_IFMT; /* Isolate file format field */ X if (x != S_IFREG) { X debug(F111,"zchki skipping:",name,x); X return(-2); X } X debug(F111,"zchki stat ok:",name,x); X X if ((x = access(name,R_OK)) < 0) { /* Is the file accessible? */ X debug(F111," access failed:",name,x); /* No */ X return(-3); X } else { X x = buf.st_size; X debug(F111," access ok:",name,x); /* Yes */ X return( (x > -1) ? x : 0 ); X } X} X X/* Z C H K O -- Check if output file can be created */ X X/* X Returns -1 if write permission for the file would be denied, 0 otherwise. X*/ Xzchko(name) char *name; { X int i, x; X char s[50], *sp; X X sp = s; /* Make a copy, get length */ X x = 0; X while ((*sp++ = *name++) != '\0') X x++; X if (x == 0) return(-1); /* If no filename, fail. */ X X debug(F101," length","",x); X for (i = x; i > 0; i--) /* Strip filename. */ X if (s[i-1] == '/') break; X X debug(F101," i","",i); X if (i == 0) /* If no path, use current directory */ X strcpy(s,"./"); X else /* Otherwise, use given one. */ X s[i] = '\0'; X X x = access(s,W_OK); /* Check access of path. */ X if (x < 0) { X debug(F111,"zchko access failed:",s,errno); X return(-1); X } else { X debug(F111,"zchko access ok:",s,x); X return(0); X } X} X X/* Z D E L E T -- Delete the named file. */ X Xzdelet(name) char *name; { X unlink(name); X} X X X/* Z R T O L -- Convert remote filename into local form */ X X/* For UNIX, this means changing uppercase letters to lowercase. */ X Xzrtol(name,name2) char *name, *name2; { X for ( ; *name != '\0'; name++) { X *name2++ = isupper(*name) ? tolower(*name) : *name; X } X *name2 = '\0'; X} X X X/* Z L T O R -- Convert filename from local format to common form. */ X Xzltor(name,name2) char *name, *name2; { X char work[100], *cp, *pp; X int dc = 0; X X strcpy(work,name); X for (cp = pp = work; *cp != '\0'; cp++) { /* strip path name */ X if (*cp == '/') { X pp = cp; X pp++; X } X else if (islower(*cp)) *cp = toupper(*cp); /* Uppercase letters */ X else if (*cp == '~') *cp = 'X'; /* Change tilde to 'X' */ X else if ((*cp == '.') && (++dc > 1)) *cp = 'X'; /* & extra dots */ X } X cp = name2; /* If nothing before dot, */ X if (*pp == '.') *cp++ = 'X'; /* insert 'X' */ X strcpy(cp,pp); X} X X X/* Z C H D I R -- Change directory */ X Xzchdir(dirnam) char *dirnam; { X char *hd; X if (*dirnam == '\0') hd = getenv("HOME"); X else hd = dirnam; X return((chdir(hd) == 0) ? 1 : 0); X} X X X/* Z H O M E -- Return pointer to user's home directory */ X Xchar * Xzhome() { X return(getenv("HOME")); X} X X/* Z X C M D -- Run a system command so its output can be read like a file */ X Xzxcmd(comand) char *comand; { X int pipes[2]; X if (pipe(pipes) != 0) return(0); /* can't make pipe, fail */ X if ((pid = fork()) == 0) { /* child */ X X/*#if BSD4*/ /* Code from Dave Tweten@AMES-NASA */ X /* readapted to use getpwuid to find login shell */ X /* -- H. Fischer */ X char *shpath, *shname, *shptr; /* to find desired shell */ X struct passwd *p; X extern struct passwd * getpwuid(); X extern int getuid(); X char *defShel = "/bin/sh"; /* default shell */ X/*#endif*/ X X close(pipes[0]); /* close input side of pipe */ X close(0); /* close stdin */ X if (open("/dev/null",0) < 0) return(0); /* replace input by null */ X X#ifndef UXIII X dup2(pipes[1],1); /* replace stdout & stderr */ X dup2(pipes[1],2); /* by the pipe */ X#else X close(1); /* simulate dup2 */ X if (dup(pipes[1]) != 1 ) X conol("trouble duping stdout in routine zxcmd\n"); X close(2); /* simulate dup2 */ X if (dup(pipes[1]) != 2 ) X conol("trouble duping stderr in routine zxcmd\n"); X#endif X X close(pipes[1]); /* get rid of this copy of the pipe */ X X/**** shptr = shname = shpath = getenv("SHELL"); /* What shell? */ X p = getpwuid( getuid() ); /* get login data */ X if ( p == (struct passwd *) NULL || !*(p->pw_shell) ) shpath = defShel; X else shpath = p->pw_shell; X shptr = shname = shpath; X while (*shptr != '\0') if (*shptr++ == '/') shname = shptr; X execl(shpath,shname,"-c",comand,0); /* Execute the command */ X X/**** execl("/bin/sh","sh","-c",comand,0); /* Execute the command */ X X exit(0); } /* just punt if it didn't work */ X X close(pipes[1]); /* don't need the output side */ X fp[ZIFILE] = fdopen(pipes[0],"r"); /* open a stream for it */ X return(1); X} X X/* Z C L O S F - wait for the child fork to terminate and close the pipe. */ X Xzclosf() { X int wstat; X fclose(fp[ZIFILE]); X fp[ZIFILE] = NULL; X while ((wstat = wait(0)) != pid && wstat != -1) ; X} X X/* Z X P A N D -- Expand a wildcard string into an array of strings */ X/* X Returns the number of files that match fn1, with data structures set up X so that first file (if any) will be returned by the next znext() call. X*/ Xzxpand(fn) char *fn; { X fcount = fgen(fn,mtchs,MAXWLD); /* Look up the file. */ X if (fcount > 0) { X mtchptr = mtchs; /* Save pointer for next. */ X } X debug(F111,"zxpand",mtchs[0],fcount); X return(fcount); X} X X X/* Z N E X T -- Get name of next file from list created by zxpand(). */ X/* X Returns >0 if there's another file, with its name copied into the arg string, X or 0 if no more files in list. X*/ Xznext(fn) char *fn; { X if (fcount-- > 0) strcpy(fn,*mtchptr++); X else *fn = '\0'; X debug(F111,"znext",fn,fcount+1); X return(fcount+1); X} X X X/* Z N E W N -- Make a new name for the given file */ X Xznewn(fn,s) char *fn, **s; { X static char buf[100]; X char *bp, *xp; X int len = 0, n = 0, d = 0, t; X X bp = buf; X while (*fn) { X *bp++ = *fn++; X len++; X } X *bp++ = '*'; /* Put a star on the end */ X *bp-- = '\0'; X X n = zxpand(buf); /* Expand the resulting wild name */ X X while (n-- > 0) { /* Find any existing name~d files */ X xp = *mtchptr++; X xp += len; X if (*xp == '~') { X t = atoi(xp+1); X if (t > d) d = t; /* Get maximum d */ X } X } X sprintf(bp,"~%d",d+1); /* Make and return name~(d+1) */ X *s = buf; X} X X/* Directory Functions for Unix, written by Jeff Damens, CUCCA, 1984. */ X X/* X * The path structure is used to represent the name to match. X * Each slash-separated segment of the name is kept in one X * such structure, and they are linked together, to make X * traversing the name easier. X */ X Xstruct path { X char npart[MAXNAMLEN]; /* name part of path segment */ X struct path *fwd; /* forward ptr */ X }; X X#define SSPACE 2000 /* size of string-generating buffer */ Xstatic char sspace[SSPACE]; /* buffer to generate names in */ Xstatic char *freeptr,**resptr; /* copies of caller's arguments */ Xstatic int remlen; /* remaining length in caller's array*/ Xstatic int numfnd; /* number of matches found */ X X/* X * splitpath: X * takes a string and splits the slash-separated portions into X * a list of path structures. Returns the head of the list. The X * structures are allocated by malloc, so they must be freed. X * Splitpath is used internally by the filename generator. X * X * Input: A string. X * Returns: A linked list of the slash-separated segments of the input. X */ X Xstruct path * Xsplitpath(p) Xchar *p; X{ X struct path *head,*cur,*prv; X int i; X head = prv = NULL; X if (*p == '/') p++; /* skip leading slash */ X while (*p != '\0') X { X cur = (struct path *) malloc(sizeof (struct path)); X cur -> fwd = NULL; X if (head == NULL) head = cur; X else prv -> fwd = cur; /* link into chain */ X prv = cur; X for (i=0; i < MAXNAMLEN && *p != '/' && *p != '\0'; i++) X cur -> npart[i] = *p++; X cur -> npart[i] = '\0'; /* end this segment */ X if (i >= MAXNAMLEN) while (*p != '/' && *p != '\0') p++; X if (*p == '/') p++; X } X return(head); X} X X/* X * fgen: X * This is the actual name generator. It is passed a string, X * possibly containing wildcards, and an array of character pointers. X * It finds all the matching filenames and stores them into the array. X * The returned strings are allocated from a static buffer local to X * this module (so the caller doesn't have to worry about deallocating X * them); this means that successive calls to fgen will wipe out X * the results of previous calls. This isn't a problem here X * because we process one wildcard string at a time. X * X * Input: a wildcard string, an array to write names to, the X * length of the array. X * Returns: the number of matches. The array is filled with filenames X * that matched the pattern. If there wasn't enough room in the X * array, -1 is returned. X * By: Jeff Damens, CUCCA, 1984. X */ X Xfgen(pat,resarry,len) Xchar *pat,*resarry[]; Xint len; X{ X struct path *head; X char scratch[100],*sptr; X head = splitpath(pat); X if (*pat == '/') X { X scratch[0] = '/'; X sptr = scratch+1; X } X else X { X strcpy(scratch,"./"); X sptr = scratch+2; X } /* init buffer correctly */ X numfnd = 0; /* none found yet */ X freeptr = sspace; /* this is where matches are copied */ X resptr = resarry; /* static copies of these so*/ X remlen = len; /* recursive calls can alter them */ X traverse(head,scratch,sptr); /* go walk the directory tree */ X for (; head != NULL; head = head -> fwd) X free(head); /* return the path segments */ X return(numfnd); /* and return the number of matches */ X} X X/* traverse: X * Walks the directory tree looking for matches to its arguments. X * The algorithm is, briefly: X * If the current pattern segment contains no wildcards, that X * segment is added to what we already have. If the name so far X * exists, we call ourselves recursively with the next segment X * in the pattern string; otherwise, we just return. X * X * If the current pattern segment contains wildcards, we open the name X * we've accumulated so far (assuming it is really a directory), then read X * each filename in it, and, if it matches the wildcard pattern segment, add X * that filename to what we have so far and call ourselves recursively on the X * next segment. X * X * Finally, when no more pattern segments remain, we add what's accumulated X * so far to the result array and increment the number of matches. X * X * Input: a pattern path list (as generated by splitpath), a string X * pointer that points to what we've traversed so far (this X * can be initialized to "/" to start the search at the root X * directory, or to "./" to start the search at the current X * directory), and a string pointer to the end of the string X * in the previous argument. X * Returns: nothing. X */ Xtraverse(pl,sofar,endcur) Xstruct path *pl; Xchar *sofar,*endcur; X{ X#ifdef BSD4 X DIR *fd; X struct direct *dirbuf; X#else X int fd; X struct direct dir_entry; X struct direct *dirbuf = &dir_entry; X#endif X struct stat statbuf; X if (pl == NULL) X { X *--endcur = '\0'; /* end string, overwrite trailing / */ X addresult(sofar); X return; X } X if (!iswild(pl -> npart)) X { X strcpy(endcur,pl -> npart); X endcur += strlen(pl -> npart); X *endcur = '\0'; /* end current string */ X if (stat(sofar,&statbuf) == 0) /* if current piece exists */ X { X *endcur++ = '/'; /* add slash to end */ X *endcur = '\0'; /* and end the string */ X traverse(pl -> fwd,sofar,endcur); X } X return; X } X/* cont'd... */ X X/*...traverse, cont'd */ X X/* segment contains wildcards, have to search directory */ X *endcur = '\0'; /* end current string */ X if (stat(sofar,&statbuf) == -1) return; /* doesn't exist, forget it */ X if ((statbuf.st_mode & S_IFDIR) == 0) return; /* not a directory, skip */ X#ifdef BSD4 X if ((fd = opendir(sofar)) == NULL) return; /* can't open, forget it */ X while (dirbuf = readdir(fd)) X#else X if ((fd = open(sofar,O_RDONLY)) < 0) return; /* can't open, forget it */ X while ( read(fd,dirbuf,sizeof dir_entry) ) X#endif X if (dirbuf->d_ino != 0 && match(pl -> npart,dirbuf->d_name)) { X char *eos; X strcpy(endcur,dirbuf->d_name); X eos = endcur + strlen(dirbuf->d_name); X *eos = '/'; /* end this segment */ X traverse(pl -> fwd,sofar,eos+1); X } X#ifdef BSD4 X closedir(fd); X#else X close(fd); X#endif X} X X/* X * addresult: X * Adds a result string to the result array. Increments the number X * of matches found, copies the found string into our string X * buffer, and puts a pointer to the buffer into the caller's result X * array. Our free buffer pointer is updated. If there is no X * more room in the caller's array, the number of matches is set to -1. X * Input: a result string. X * Returns: nothing. X */ X Xaddresult(str) Xchar *str; X{ X int l; X if (strncmp(str,"./",2) == 0) str += 2; X if (--remlen < 0) { X numfnd = -1; X return; X } X l = strlen(str) + 1; /* size this will take up */ X if ((freeptr + l) > &sspace[SSPACE]) { X numfnd = -1; /* do not record if not enough space */ X return; X } X strcpy(freeptr,str); X *resptr++ = freeptr; X freeptr += l; X numfnd++; X} X Xiswild(str) Xchar *str; X{ X char c; X while ((c = *str++) != '\0') X if (c == '*' || c == '?') return(1); X return(0); X} X X/* X * match: X * pattern matcher. Takes a string and a pattern possibly containing X * the wildcard characters '*' and '?'. Returns true if the pattern X * matches the string, false otherwise. X * by: Jeff Damens, CUCCA X * X * Input: a string and a wildcard pattern. X * Returns: 1 if match, 0 if no match. X */ X Xmatch(pattern,string) char *pattern,*string; { X char *psave,*ssave; /* back up pointers for failure */ X psave = ssave = NULL; X while (1) { X for (; *pattern == *string; pattern++,string++) /* skip first */ X if (*string == '\0') return(1); /* end of strings, succeed */ X if (*string != '\0' && *pattern == '?') { X pattern++; /* '?', let it match */ X string++; X } else if (*pattern == '*') { /* '*' ... */ X psave = ++pattern; /* remember where we saw it */ X ssave = string; /* let it match 0 chars */ X } else if (ssave != NULL && *ssave != '\0') { /* if not at end */ X /* ...have seen a star */ X string = ++ssave; /* skip 1 char from string */ X pattern = psave; /* and back up pattern */ X } else return(0); /* otherwise just fail */ X } X} !FUNKY!STUFF!