/* G U N I X I O -- UNIX i/o module for gkermit */ /* UNIX i/o functions for gkermit. Author: Frank da Cruz The Kermit Project Columbia University 612 West 115th Street New York NY 10025-7799 USA http://www.columbia.edu/kermit/ kermit@columbia.edu Copyright (C) 1999, The Trustees of Columbia University in the City of New York. 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 x = 0; 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 */ } 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); }