/* G U N I X I O -- UNIX i/o module for gkermit */ /* UNIX i/o functions for gkermit. Author: Frank da Cruz Originally at: The Kermit Project Columbia University 612 West 115th Street New York NY 10025-7799 USA http://www.columbia.edu/kermit/ kermit@columbia.edu As of October 2011: The New Open-Source Kermit Project Bronx NY http://kermitproject.org kermit@kermitproject.org Last update: Mon Nov 15 09:48:53 2021 Suppress warning about write(1,"\015\012",2); in ttres(). Copyright (C) 1999, 2021 The Trustees of Columbia University in the City of New York. Issued under the GNU General Public License as it existed in 1999. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* CONTENTS... Console Output: tmsg - Type a message tmsgl - Type a line Communication Device: ttopen - Open ttpkt - Put in packet mode ttres - Restore normal mode ttinl - Input a raw packet ttol - Send a packet ttchk - Check if input ready ttflui - Flush input buffer File: zchki - See if file can be opened for input zopeni - Open input file zopeno - Open output file zclosi - Close input file zcloso - Close output file zrtol - Remote-to-Local filename conversion zltor - Local-to-Remote filename conversion zgetc - Get character from input file */ #include /* Standard input/output */ #ifdef POSIX #include /* Terminal modes */ #else #ifdef SYSV #include #else #include #endif /* SYSV */ #endif /* POSIX */ #include /* Character types */ #include /* Needed e.g. by */ #include /* Interrupts */ #include /* Longjumps */ #include /* File exist, file size */ #include /* Error symbols */ #include "gkermit.h" /* gkermit definitions */ /* All versions of HP-UX need Xon/Xoff */ #ifdef hpux /* HP-UX Pre-7.00 */ #ifndef __hpux #define __hpux #endif /* __hpux */ #endif /* hpux */ #ifdef __hpux /* HP-UX 7.00 and later */ #ifndef SETXONXOFF #define SETXONXOFF #endif /* SETXONXOFF */ #endif /* __hpux */ #ifdef NOXONXOFF /* -DNOXONXOFF overrides */ #ifdef SETXONXOFF #undef SETXONXOFF #endif /* SETXONXOFF */ #endif /* NOXONXOFF */ #ifndef TINBUFSIZ /* read() inpbut buffer */ #ifdef USE_GETCHAR #define TINBUFSIZ 0 /* getchar() has its own */ #else #ifdef SMALL #define TINBUFSIZ 240 #else #define TINBUFSIZ 4080 #endif /* SMALL */ #endif /* USE_GETCHAR */ #endif /* TINBUFSIZ */ #ifndef DUMBIO #ifndef USE_GETCHAR #ifndef NOFCNTL_H /* For nonblocking buffered read() */ #ifdef SYS_FCNTL_H #include #else #include #ifndef O_NDELAY #ifdef O_NONBLOCK #define O_NDELAY O_NONBLOCK #endif /* O_NONBLOCK */ #endif /* O_NDELAY */ #endif /* SYS_FCNTL_H */ #endif /* NOFCNTL_H */ #endif /* USE_GETCHAR */ #endif /* DUMBIO */ #ifdef O_NDELAY /* For System V R3 and earlier */ #ifndef EWOULDBLOCK #ifdef EAGAIN #define EWOULDBLOCK EAGAIN #endif /* EAGAIN */ #endif /* EWOULDBLOCK */ #endif /* O_NDELAY */ #ifndef DUMBIO /* To force single-char read/write */ #ifndef USE_GETCHAR #ifndef O_NDELAY #define DUMBIO #endif /* O_NDELAY */ #endif /* USE_GETCHAR */ #endif /* DUMBIO */ /* Header file deficiencies section... */ #ifndef R_OK #define R_OK 4 #endif /* R_OK */ #ifndef W_OK #define W_OK 2 #endif /* W_OK */ #ifndef _IFMT #ifdef S_IFMT #define _IFMT S_IFMT #else #define _IFMT 0170000 #endif /* S_IFMT */ #endif /* _IFMT */ #ifndef S_ISREG #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif /* S_ISREG */ /* External variables */ extern int literal; /* Literal filenames */ extern int quiet; /* No messages */ extern int keep; /* Keep incomplete files */ extern int streamok; /* OK to offer streaming */ extern int nomodes; /* Don't get/set tty modes */ extern int xonxoff; /* Set Xon/Xoff */ extern int noxonxoff; /* Don't set Xon/Xoff */ extern FILE * db; /* Debug log file */ /* Exported variables */ FILE *ifp, *ofp; /* Input and output file pointers */ char zinbuf[MAXRECORD+1]; /* File input buffer */ int zincnt = 0; /* count */ char * zinptr = NULL; /* and pointer. */ /* Private global variables */ static int havemodes = 0; /* Have obtained terminal modes */ static int ttflags = -1; /* Terminal flags */ static int nonblock = 0; /* Nonblocking i/o enabled */ static char tinbuf[TINBUFSIZ+16]; /* Communications input buffer */ static char * tinptr = NULL; /* Pointer to current item */ static int tincnt = 0; /* Buffer count */ static int tlast = 0; /* Last item in buffer */ static int xparity = 0; /* Parity in use, 0 = none */ static int raw = 0; /* Terminal rawmode flag */ static char work[MAXPATHLEN+1]; /* Filename conversion buffer */ /* Terminal mode structs */ #ifdef POSIX /* POSIX */ static struct termios ttold, ttraw; #else #ifdef SYSV /* System V */ static struct termio ttold = {0}; static struct termio ttraw = {0}; #else #ifdef BSD /* 4.2 BSD or UNIX V7 */ static struct sgttyb ttold, ttraw; #endif /* BSD */ #endif /* SYSV */ #endif /* POSIX */ static jmp_buf jbuf; /* Longjump buffer for timeouts */ /* Functions... */ SIGTYP doexit(x) int x; { /* Exit routine */ if (x) /* If failure */ ttflui(); /* flush pending junk we won't read */ ttres(); /* Reset the communication device */ #ifdef F_SETFL if (ttflags > -1) /* Restore its flags */ fcntl(0,F_SETFL,ttflags); #endif /* F_SETFL */ if (debug) { fprintf(db,"exit %d\n",x); fclose(db); } exit(x); } VOID sysinit() { /* To be run first thing */ #ifdef F_SETFL ttflags = fcntl(0,F_GETFL,0); /* Get and save stdin flags */ #endif /* F_SETFL */ #ifdef SIGINT signal(SIGINT,SIG_IGN); /* Ignore interrupts */ #endif /* SIGINT */ #ifdef SIGTSTP signal(SIGTSTP,SIG_IGN); #endif /* SIGTSTP */ #ifdef SIGQUIT signal(SIGQUIT,SIG_IGN); #endif /* SIGQUIT */ signal(SIGHUP,doexit); /* Go here on hangup */ } /* Console Functions */ #ifdef COMMENT /* (not used) */ VOID tmsg(s) char *s; { /* tmsg() */ if (!quiet) fprintf(stderr,"%s",s); /* Type message on the screen. */ } #endif /* COMMENT */ VOID tmsgl(s) char *s; { /* tmsgl() */ if (!quiet) { if (raw) fprintf(stderr,"%s\r\n",s); /* Type message with CRLF */ else fprintf(stderr,"%s\n",s); } } /* Debugging functions */ VOID logerr(s) char * s; { /* Log text and errno */ if (!s) s = ""; if (!debug) return; if (db) fprintf(db,"%s: errno = %d\n",s,errno); } /* Parity function */ char #ifdef __STDC__ dopar(char ch) #else dopar(ch) char ch; #endif /* __STDC__ */ { /* Do parity */ unsigned int a; if (!xparity) return(ch); else ch &= 0177; switch (xparity) { case 'm': return(ch | 128); /* Mark */ case 's': return(ch & 127); /* Space */ case 'o': /* Odd (fall thru) */ case 'e': /* Even */ a = (ch & 15) ^ ((ch >> 4) & 15); a = (a & 3) ^ ((a >> 2) & 3); a = (a & 1) ^ ((a >> 1) & 1); if (xparity == 'o') a = 1 - a; /* Switch sense for odd */ return(ch | (a << 7)); default: return(ch); } } /* Communication functions */ int ttopen(ttname) char *ttname; { /* "Open" the communication device */ if (debug) { /* Vital statistics for debug log */ #ifdef __STDC__ fprintf(db,"ttopen __STDC__\n"); #endif /* __STDC__ */ #ifdef SIG_V fprintf(db,"ttopen SIG_V\n"); #else #ifdef SIG_I fprintf(db,"ttopen SIG_I\n"); #endif /* SIG_I */ #endif /* SIG_V */ #ifdef USE_GETCHAR fprintf(db,"ttopen getchar/putchar\n"); #ifdef BUFSIZ fprintf(db,"ttopen BUFSIZ = %d\n", BUFSIZ); #endif /* BUFSIZ */ #else #ifdef DUMBIO fprintf(db,"ttopen single-byte read/write\n"); #else fprintf(db,"ttopen nonblocking read/write\n"); #endif /* DUMBIO */ #endif /* USE_GETCHAR */ fprintf(db,"ttopen TINBUFSIZ = %d\n", TINBUFSIZ); #ifdef __hpux fprintf(db,"ttopen __hpux\n"); #endif /* __hpux */ #ifdef pdp11 fprintf(db,"ttopen pdp11\n"); #endif /* pdp11 */ #ifdef SETXONXOFF fprintf(db,"ttopen SETXONXOFF\n"); #endif /* SETXONXOFF */ fprintf(db,"ttopen xonxoff = %d\n",xonxoff); fprintf(db,"ttopen noxonxoff = %d\n",noxonxoff); fprintf(db,"ttopen ttflags %d\n",ttflags); fprintf(db,"ttopen nomodes %d\n",nomodes); } if (nomodes) { /* If external protocol */ #ifdef SIGINT /* exit on interrupts */ signal(SIGINT,doexit); #endif /* SIGINT */ #ifdef SIGTSTP signal(SIGTSTP,doexit); #endif /* SIGTSTP */ #ifdef SIGQUIT signal(SIGQUIT,doexit); #endif /* SIGQUIT */ return(0); } #ifndef DUMBIO #ifndef USE_GETCHAR #ifdef O_NDELAY #ifdef F_SETFL if (ttflags != -1) { /* Set nonbocking i/o on stdin */ errno = 0; if (fcntl(0, F_SETFL,ttflags|O_NDELAY) == -1) logerr("ttopen fcntl(0,F_SETFL,O_NDELAY)"); else nonblock = 1; } #endif /* F_SETFL */ #endif /* O_NDELAY */ #endif /* USE_GETCHAR */ #endif /* DUMBIO */ if (!nonblock) /* No streaming without */ streamok = -1; /* nonblocking reads */ if (debug) fprintf(db,"ttopen nonblock = %d\n", nonblock); #ifdef POSIX tcgetattr(0,&ttold); /* Get stdin device attributes */ tcgetattr(0,&ttraw); #else #ifdef SYSV ioctl(0,TCGETA,&ttold); ioctl(0,TCGETA,&ttraw); #else #ifdef BSD gtty(0,&ttold); gtty(0,&ttraw); #endif /* BSD */ #endif /* SYSV */ #endif /* POSIX */ havemodes++; return(0); } int ttpkt(parity) int parity; { /* Put comm device in packet mode */ #ifdef BSD int x; #endif /* BSD */ xparity = parity; /* Make local copy of parity */ if (nomodes) return(0); #ifdef SVORPOSIX /* System V or POSIX section... */ ttraw.c_iflag |= IGNPAR; ttraw.c_lflag &= ~(ICANON|ECHO); ttraw.c_lflag &= ~ISIG; ttraw.c_lflag |= NOFLSH; #ifdef SETXONXOFF if (!noxonxoff) { ttraw.c_iflag |= (IXON|IXOFF); if (debug) fprintf(db,"ttpkt SVORPOSIX Xon/Xoff\n"); } #else if (xonxoff) { if (debug) fprintf(db,"ttpkt SVORPOSIX Xon/Xoff\n"); ttraw.c_iflag |= (IXON|IXOFF); } #endif /* SETXONXOFF */ #ifdef IEXTEN ttraw.c_lflag &= ~IEXTEN; #endif /* IEXTEN */ #ifdef POSIX ttraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|INPCK|ISTRIP); #else ttraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|INPCK|ISTRIP|IXANY); #endif /* POSIX */ ttraw.c_oflag &= ~OPOST; ttraw.c_cflag &= ~(CSIZE); ttraw.c_cflag |= (CS8|CREAD|HUPCL); ttraw.c_cflag &= ~(PARENB); #ifndef VEOF ttraw.c_cc[4] = 1; #else #ifdef VMIN ttraw.c_cc[VMIN] = 1; #endif /* VMIN */ #endif /* VEOF */ #ifndef VEOL ttraw.c_cc[5] = 0; #else #ifdef VTIME ttraw.c_cc[VTIME] = 0; #endif /* VTIME */ #endif /* VEOL */ #ifdef VINTR ttraw.c_cc[VINTR] = 0; #endif /* VINTR */ #ifdef POSIX if (tcsetattr(0,TCSADRAIN,&ttraw) < 0) return(-1); #else if (ioctl(0,TCSETAW,&ttraw) < 0) return(-1); #ifdef SYSV #endif /* SYSV */ #endif /* POSIX */ #else /* Not SVORPOSIX */ #ifdef BSD ttraw.sg_flags |= RAW; /* BSD/V7 raw (binary) mode */ #ifdef SETXONXOFF if (!noxonxoff) { ttraw.sg_flags |= TANDEM; if (debug) fprintf(db,"ttpkt BSD Xon/Xoff\n"); } #else if (xonxoff) { ttraw.sg_flags |= TANDEM; if (debug) fprintf(db,"ttpkt BSD Xon/Xoff\n"); } #endif /* SETXONXOFF */ ttraw.sg_flags &= ~(ECHO|CRMOD); /* No echo, etc */ if (stty(0,&ttraw) < 0) return(-1); /* Set modes */ #else system("stty raw -echo"); #endif /* BSD */ #endif /* SVORPOSIX */ raw = 1; /* Flag we're now in raw mode */ return(0); } int ttres() { /* Reset terminal */ int dummy = 0; /* Suppress warning about write() */ int x = 0; /* API return codes */ if (havemodes) { /* Restore old modes */ #ifdef POSIX x = tcsetattr(0,TCSADRAIN,&ttold); #else #ifdef SYSV sleep(1); /* Let output finish */ x = ioctl(0,TCSETAW,&ttold); #else #ifdef BSD sleep(1); /* Let output finish */ x = stty(0,&ttold); #else x = system("stty -raw echo"); #endif /* BSD */ #endif /* SYSV */ #endif /* POSIX */ } dummy = (int) write(1,"\015\012",2); raw = 0; return(x); } int ttchk() { /* Check if input ready */ int x = 0; if (nonblock) { /* Try to read */ errno = 0; x = read(0,&tinbuf[tlast],TINBUFSIZ-tlast); #ifdef EXTRADEBUG fprintf(db,"ttchk read %d errno = %d\n",x,errno); #endif /* EXTRADEBUG */ #ifdef EWOULDBLOCK if (x < 0 && errno == EWOULDBLOCK) /* Nothing to read */ x = 0; #endif /* EWOULDBLOCK */ if (x < 0) /* Fatal i/o error */ return(-1); } tincnt += x; /* Buffer bookkeeping */ tlast += x; return(x + tincnt); /* How much is waiting to be read */ } int ttflui() { /* Flush comm device input buffer */ #ifdef BSD long n = 1; /* Specify read queue */ #endif /* BSD */ int x; tincnt = 0; /* Our own buffer */ tlast = 0; tinptr = tinbuf; errno = 0; #ifdef POSIX x = tcflush(0,TCIFLUSH); /* kernel/driver buffers */ #else #ifdef SYSV x = ioctl(0,TCFLSH,0); #else #ifdef BSD x = ioctl(0,TIOCFLUSH,&n); #endif /* BSD */ #endif /* SYSV */ #endif /* POSIX */ if (debug) fprintf(db,"ttflui = %d, errno = %d\n",x,errno); return(x); } SIGTYP timerh(dummy) int dummy; { /* Timeout handler */ longjmp(jbuf,1); SIGRETURN; } /* ttinl() - Read a raw packet. Call with: dest - where to put it max - maximum length timo - timeout (seconds, 0 = none) eol - packet terminator turn - half-duplex line turnaround character to wait for, 0 = none Returns length obtained, or -1 if error or timeout, -2 on disconnection. */ #ifndef DEBUGWRAP #define DEBUGWRAP 48 #endif /* DEBUGWRAP */ int #ifdef __STDC__ ttinl(char * dest, int max, int timo, char eol, char soh, int turn) #else ttinl(dest,max,timo,eol,soh,turn) int max, timo, turn; char eol, soh, *dest; #endif /* __STDC__ */ { int n = 0, x = 0, flag = 0, rc = 0, ccn = 0; /* Local variables */ char c = NUL; int havelen = 0, pktlen = 0, lplen = 0; #ifdef USE_GETCHAR if (debug) fprintf(db,"ttinl getchar timo = %d\n",timo); #else if (debug) fprintf(db,"ttinl read timo = %d\n",timo); #endif /* USE_GETCHAR */ *dest = NUL; /* Clear destination buffer */ if (timo) { signal(SIGALRM,timerh); /* Enable timer interrupt */ alarm(timo); /* Set it. */ } if (setjmp(jbuf)) { /* Timer went off? */ if (debug) fprintf(db,"ttinl timeout\n"); rc = -1; /* Yes, set this return code. */ } else { /* Otherwise... */ while (1) { /* Read until we have a packet */ #ifdef DUMBIO x = read(0,&c,1); /* Dumb blocking read byte loop */ if (x < 0) { logerr("ttinl XX read 1"); rc = -2; } #else #ifdef USE_GETCHAR errno = 0; x = getchar(); /* Buffered read with getchar() */ if (x == EOF) { if (errno == EINTR) continue; logerr("ttinl getchar"); rc = -2; } c = x; #else /* USE_GETCHAR */ #ifdef O_NDELAY if (nonblock) { /* Buffered nonblocking read() */ int x; if (tincnt < 1) { /* Need to fill our buffer */ errno = 0; tincnt = read(0,tinbuf,TINBUFSIZ); if (tincnt > -1) tlast = tincnt; if (debug) fprintf(db,"ttinl nonblock tincnt=%d errno=%d\n", tincnt,errno); if (tincnt == 0 || errno == EWOULDBLOCK) { #ifdef F_SETFL /* Go back to blocking and wait for 1 char */ if (ttflags != -1) { errno = 0; x = fcntl(0, F_SETFL, ttflags & ~O_NDELAY); if (x == -1 || errno) logerr("ttinl fcntl O_NDELAY off"); errno = 0; tincnt = read(0,tinbuf,1); if (tincnt < 1 || errno) logerr("ttinl BL read"); errno = 0; fcntl(0, F_SETFL, ttflags | O_NDELAY); if (x == -1 || errno) logerr("ttinl fcntl O_NDELAY on"); } if (tincnt == 0) { /* Check results */ continue; } if (tincnt < 0) { /* I/O error */ rc = -2; goto xttinl; } if (debug) fprintf(db,"ttinl blocking read %d\n",tincnt); #else /* No other form of sleeping is portable */ sleep(1); continue; #endif /* F_SETFL */ } else if (tincnt < 0) { rc = -2; goto xttinl; } tinptr = tinbuf; } c = *tinptr++; tincnt--; } else { #endif /* O_NDELAY */ x = read(0,&c,1); /* Dumb read byte loop */ if (x < 0) { logerr("ttinl XX read 1"); rc = -2; } #ifdef O_NDELAY } #endif /* O_NDELAY */ #endif /* USE_GETCHAR */ #endif /* DUMBIO */ if (rc < 0) break; if (xparity) /* Strip parity */ c &= 0x7f; #ifdef COMMENT /* Only uncomment in emergencies */ if (debug) fprintf(db,"ttinl char=%c flag=%d tincnt=%d\n",c,flag,tincnt); #endif /* COMMENT */ if (c == '\03') { /* Got ^C, count it. */ if (++ccn > 2) { /* If more than 2, let them out */ fprintf(stderr,"^C..."); ttres(); if (debug) fprintf(db,"ttinl interrupted\n"); dest[n = 0] = NUL; rc = -9; goto xttinl; } } else /* Not ^C so reset counter*/ ccn = 0; if (!flag && (c != soh)) /* Look for SOH */ continue; /* Skip stuff between packets */ flag = 1; /* Have SOH */ if (n >= max) { if (debug) fprintf(db,"ttinl overflow\n"); rc = -2; goto xttinl; } dest[n++] = c; /* Store the character */ #ifdef USE_EOL /* Use EOL to determine end of packet */ if (c == eol) { dest[n] = NUL; break; } #else /* Use length field for framing */ if (!havelen) { if (n == 2) { pktlen = xunchar(dest[1] & 0x7f); if (pktlen > 1) { if (debug) fprintf(db,"ttinl length = %d\n",pktlen); havelen = 1; } } else if (n == 5 && pktlen == 0) { lplen = xunchar(dest[4] & 0x7f); } else if (n == 6 && pktlen == 0) { pktlen = lplen * 95 + xunchar(dest[5] & 0x7f) + 5; if (debug) fprintf(db,"ttinl length = %d\n",pktlen); havelen = 1; } } if (havelen && (n > pktlen+1)) { if (turn && c != turn) /* Wait for turnaround char */ continue; dest[n] = NUL; /* Null-terminate whatever we got */ break; } #endif /* USE_EOL */ } } xttinl: /* Common exit point */ if (timo) { alarm(0); /* Turn off the alarm */ signal(SIGALRM,SIG_IGN); /* and associated interrupt */ } if (debug && n > 0) { /* Log packet */ #ifndef FULLPACKETS if (n > DEBUGWRAP) { /* Truncate if it would wrap */ dest[n] = NUL; /* in case of interruption */ c = dest[DEBUGWRAP]; dest[DEBUGWRAP] = NUL; fprintf(db,"PKT<-[^A%s...](%d) rc=%d\n",&dest[1],n,rc); dest[DEBUGWRAP] = c; } else #endif /* FULLPACKETS */ fprintf(db,"PKT<-[^A%s](%d) rc=%d\n",&dest[1],n,rc); } if (rc == -9) /* Interrupted by user */ doexit(1); else if (rc > -1) rc = n; return(rc); /* Return length, or failure. */ } int ttol(s,len) int len; char *s; { /* Output string s of given length */ register int i = 0, n = 0, m = 0; int partial = 0; n = len; if (n < 0) { if (debug) fprintf(db,"ttol len = %d\n",n); return(-1); } if (xparity) { /* Add parity if requested */ for (i = 0; i < n; i++) s[i] = dopar(s[i]); } if (debug) { /* Log the packet if requested */ char c; #ifndef FULLPACKETS if (n > DEBUGWRAP) { c = s[DEBUGWRAP]; s[DEBUGWRAP] = NUL; fprintf(db,"PKT->[^A%s...](%d)\n",&s[1],n); s[DEBUGWRAP] = c; } else { #endif /* FULLPACKETS */ c = s[n-1]; s[n-1] = NUL; fprintf(db,"PKT->[^A%s](%d)\n",&s[1],n); s[n-1] = c; #ifndef FULLPACKETS } #endif /* FULLPACKETS */ } #ifdef USE_GETCHAR { /* Send the packet with putchar() */ register CHAR c; register int i; for (i = 0; i < n; i++) { c = *s++; if (putchar(c) == EOF) { logerr("ttol putchar"); return(-1); } } } fflush(stdout); /* Push it out */ return(n); #else while (n > 0) { /* Send the packet with write() */ i = write(1,&s[m],n); /* Allowing for partial results */ if (i < 0) { if (errno == EWOULDBLOCK) /* and even no results at all.. */ continue; logerr("ttol write"); return(-1); } if (i == n) break; partial++; m += i; if (debug) fprintf(db,"ttol partial write %d (%d/%d)\n",i,m,len); n -= i; } if (partial) { m += i; if (debug) fprintf(db,"ttol partial write %d (%d/%d)\n",i,m,len); if (m != len) { if (debug) fprintf(db,"ttol foulup %d != %d\n",m,len); return(-1); } } return(len); #endif /* USE_GETCHAR */ } /* File Functions */ char ofile[MAXPATHLEN]; /* Output filename */ long filelength = -1L; long zchki(fn) char * fn; { /* Check if file is readable */ struct stat buf; if (!fn) return(-1); if (stat(fn,&buf) < 0) return(-1); errno = 0; if (access(fn,R_OK) < 0) { if (debug) fprintf(db,"zchki access %s errno = %d\n",fn,errno); return(-1); } if (!S_ISREG(buf.st_mode)) { if (debug) fprintf(db,"zchki %s is a directory",fn); return(-2); } return(buf.st_size); } int zchko(fn) char *fn; { /* Check write access */ int i, x; char * s; if (!fn) /* Defend against empty name */ fn = ""; if (!*fn) return(-1); if (!strcmp(fn,"/dev/null")) /* Null device is OK. */ return(0); if ((x = zchki(fn)) == -2) /* An existing directory? */ return(-1); s = fn; if (x < 0) { /* If file does not exist */ strncpy(work,fn,MAXPATHLEN); work[MAXPATHLEN] = NUL; s = work; for (i = (int)strlen(s); i > 0; i--) { /* Strip filename from right */ if (s[i-1] == '/') { /* and check its directory */ s[i-1] = NUL; break; } } if (i == 0) s = "."; } errno = 0; x = access(s,W_OK); /* Check access of path. */ if (debug) fprintf(db,"zchko(%s) x = %d errno = %d\n",s,x,errno); return((x < 0) ? -1 : 0); /* and return. */ } int zopeni(name) char *name; { /* Open existing file for input */ ifp = fopen(name,"r"); if (debug) fprintf(db,"zopeni %s: %d\n",name, ifp ? 0 : errno); filelength = zchki(name); if (filelength < 0) return((int)filelength); zincnt = 0; zinptr = zinbuf; return((ifp == NULL) ? -1 : 0); } int zopeno(name) char *name; { /* Open new file for output */ errno = 0; ofp = fopen(name,"w"); if (debug) fprintf(db,"zopeno %s: %d\n",name, ofp ? 0 : errno); if (ofp) { strncpy(ofile,name,MAXPATHLEN); ofile[MAXPATHLEN-1] = NUL; return(0); } else return(-1); } VOID /* Local to remote file name */ zltor(lclnam,pktnam,maxlen) char *lclnam, *pktnam; int maxlen; { char *p, *np = NULL, *cp, *pp, c; char *dotp = NULL; char *dirp = NULL; int n = 0; if (debug) fprintf(db,"zltor %s: maxlen = %d, literal = %d\n", lclnam,maxlen,literal); if (literal) { p = lclnam; dirp = p; while (*p) { if (*p == '/') dirp = p+1; p++; } strncpy(pktnam,dirp,maxlen); } else { for (p = lclnam; *p; p++) { /* Point to name part */ if (*p == '/') np = p; } if (np) { np++; if (!*np) np = lclnam; } else np = lclnam; if (debug) fprintf(db,"zltor np %s\n",np); pp = work; /* Output buffer */ for (cp = np, n = 0; *cp && n < maxlen; cp++,n++) { c = *cp; if (islower(c)) /* Uppercase letters */ *pp++ = toupper(c); /* Change tilde to hyphen */ else if (c == '~') *pp++ = '-'; else if (c == '#') /* Change number sign to 'X' */ *pp++ = 'X'; else if (c == '*' || c == '?') /* Change wildcard chars to 'X' */ *pp++ = 'X'; else if (c == ' ') /* Change space to underscore */ *pp++ = '_'; else if (c < ' ') /* Change space and controls to 'X' */ *pp++ = 'X'; else if (c == '.') { /* Change dot to underscore */ dotp = pp; /* Remember where we last did this */ *pp++ = '_'; } else { if (c == '/') dirp = pp; *pp++ = c; } } *pp = NUL; /* Tie it off. */ if (dotp > dirp) *dotp = '.'; /* Restore last dot in file name */ cp = pktnam; /* If nothing before dot, */ if (*work == '.') *cp++ = 'X'; /* insert 'X' */ strncpy(cp,work,maxlen); cp[maxlen-1] = NUL; } if (debug) fprintf(db,"zltor result: %s\n",pktnam); } int zbackup(fn) char * fn; { /* Back up existing file */ struct stat buf; int i, j, k, x, state, flag; char *p, newname[MAXPATHLEN+12]; if (!fn) /* Watch out for null pointers. */ return(-1); if (!*fn) /* And empty names. */ return(-1); if (stat(fn,&buf) < 0) /* If file doesn't exist */ return(0); /* no need to back it up. */ i = strlen(fn); /* Get length */ if (i > MAXPATHLEN) /* Guard buffer */ i = MAXPATHLEN; if (debug) fprintf(db,"zbackup A %s: %d\n", fn, i); strncpy(work,fn,MAXPATHLEN); /* Make pokeable copy of name */ work[MAXPATHLEN] = NUL; p = work; /* Strip any backup prefix */ i--; for (flag = state = 0; (!flag && (i > 0)); i--) { switch (state) { case 0: /* State 0 - final char */ if (p[i] == '~') /* Is tilde */ state = 1; /* Switch to next state */ else /* Otherwise */ flag = 1; /* Quit - no backup suffix. */ break; case 1: /* State 1 - digits */ if (p[i] == '~' && p[i-1] == '.') { /* Have suffix */ p[i-1] = NUL; /* Trim it */ flag = 1; /* done */ } else if (p[i] >= '0' && p[i] <= '9') { /* In number part */ continue; /* Keep going */ } else { /* Something else */ flag = 1; /* Not a backup suffix - quit. */ } break; } } if (debug) fprintf(db,"zbackup B %s\n", p); if (!p[0]) p = fn; j = strlen(p); strncpy(newname,p,MAXPATHLEN); for (i = 1; i < 1000; i++) { /* Search from 1 to 999 */ if (i < 10) /* Length of numeric part of suffix */ k = 1; else if (i < 100) k = 2; else k = 3; x = j; /* Where to write suffix */ if ((x + k + 3) > MAXPATHLEN) x = MAXPATHLEN - k - 3; sprintf(&newname[x],".~%d~",i); /* Make a backup name */ if (stat(newname,&buf) < 0) { /* If it doesn't exist */ errno = 0; if (link(fn,newname) < 0) { /* Rename old file to backup name */ if (debug) fprintf(db,"zbackup failed: link(%s): %d\n",newname,errno); return(-1); } else if (unlink(fn) < 0) { if (debug) fprintf(db,"zbackup failed: unlink(%s): %d\n",fn,errno); return(-1); } else { if (debug) fprintf(db,"zbackup %s: OK\n",newname); return(0); } } } if (debug) fprintf(db,"zbackup failed: all numbers used\n"); return(-1); } int /* Remote to local filename */ zrtol(pktnam,lclnam,warn,maxlen) char *pktnam, *lclnam; int warn, maxlen; { int acase = 0, flag = 0, n = 0; char * p; if (literal) { strncpy(lclnam,pktnam,maxlen); } else { for (p = lclnam; *pktnam != '\0' && n < maxlen; pktnam++) { if (*pktnam > SP) flag = 1; /* Strip leading blanks and controls */ if (flag == 0 && *pktnam < '!') continue; if (isupper(*pktnam)) /* Check for mixed case */ acase |= 1; else if (islower(*pktnam)) acase |= 2; *p++ = *pktnam; n++; } *p-- = NUL; /* Terminate */ while (*p < '!' && p > lclnam) /* Strip trailing blanks & controls */ *p-- = '\0'; if (!*lclnam) { /* Nothing left? */ strncpy(lclnam,"NONAME",maxlen); /* do this... */ } else if (acase == 1) { /* All uppercase? */ p = lclnam; /* So convert all letters to lower */ while (*p) { if (isupper(*p)) *p = tolower(*p); p++; } } } if (warn) { if (zbackup(lclnam) < 0) return(-1); } return(0); } int zclosi() { /* Close input file */ int rc; rc = (fclose(ifp) == EOF) ? -1 : 0; ifp = NULL; return(rc); } int zcloso(cx) int cx; { /* Close output file */ int rc; rc = (fclose(ofp) == EOF) ? -1 : 0; if (debug) fprintf(db,"zcloso(%s) cx = %d keep = %d\n", ofile, cx, keep); if (cx && !keep ) unlink(ofile); /* Delete if incomplete */ ofp = NULL; return(rc); } int zfillbuf(text) int text; { /* Refill input file buffer */ if (zincnt < 1) { /* Nothing in buffer - must refill */ if (text) { /* Text mode needs LF/CRLF handling */ int c; /* Current character */ for (zincnt = 0; /* Read a line */ zincnt < MAXRECORD - 1 && (c = getc(ifp)) != EOF && c != '\n'; zincnt++ ) { zinbuf[zincnt] = c; } if (c == '\n') { /* Have newline. */ zinbuf[zincnt++] = '\r'; /* Substitute CRLF */ zinbuf[zincnt++] = c; } } else { /* Binary - just read raw buffers */ zincnt = fread(zinbuf, sizeof(char), MAXRECORD, ifp); } zinbuf[zincnt] = NUL; /* Terminate. */ if (zincnt == 0) /* Check for EOF */ return(-1); zinptr = zinbuf; /* Not EOF - reset pointer */ } #ifdef EXTRADEBUG /* Voluminous debugging */ if (debug) fprintf(db,"zfillbuf (%s) zincnt = %d\n", text ? "text" : "binary", zincnt ); #endif /* EXTRADEBUG */ zincnt--; /* Return first byte. */ return(*zinptr++ & 0xff); }