1175 lines
29 KiB
C
1175 lines
29 KiB
C
/* 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 <stdio.h> /* Standard input/output */
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#ifdef POSIX
|
|
#include <termios.h> /* Terminal modes */
|
|
#else
|
|
#ifdef SYSV
|
|
#include <termio.h>
|
|
#else
|
|
#include <sgtty.h>
|
|
#endif /* SYSV */
|
|
#endif /* POSIX */
|
|
|
|
#include <ctype.h> /* Character types */
|
|
#include <sys/types.h> /* Needed e.g. by <stat.h> */
|
|
#include <signal.h> /* Interrupts */
|
|
#include <setjmp.h> /* Longjumps */
|
|
#include <sys/stat.h> /* File exist, file size */
|
|
#include <errno.h> /* 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 <sys/fcntl.h>
|
|
#else
|
|
#include <fcntl.h>
|
|
#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);
|
|
}
|