summaryrefslogtreecommitdiff
path: root/unixio.c
diff options
context:
space:
mode:
Diffstat (limited to 'unixio.c')
-rw-r--r--unixio.c627
1 files changed, 627 insertions, 0 deletions
diff --git a/unixio.c b/unixio.c
new file mode 100644
index 0000000..6a7e0f0
--- /dev/null
+++ b/unixio.c
@@ -0,0 +1,627 @@
+/* Sample system-dependent communications i/o routines for embedded Kermit. */
+
+/*
+ Author: Frank da Cruz.
+ Copyright (C) 1995, 2011.
+ Trustees of Columbia University in the City of New York.
+ All rights reserved.
+ See kermit.c for license.
+*/
+
+/*
+ The sample i/o routines for UNIX that provide packet i/o
+ functions on the console (login) device.
+ Copy this file, rename it appropriately, and replace the contents
+ of each routine appropriately for your platform.
+
+ Device i/o:
+
+ int devopen() Communications device - open
+ int pktmode() Communications device - enter/exit packet mode
+ int readpkt() Communications device - read a packet
+ int tx_data() Communications device - send data
+ int devclose() Communications device - close
+ int inchk() Communications device - check if bytes are ready to read
+
+ File i/o:
+
+ int openfile() File - open for input or output
+ ULONG fileinfo() Get input file modtime and size
+ int readfile() Input file - read data
+ int writefile() Output file - write data
+ int closefile() Input or output file - close
+
+ Full definitions below, prototypes in kermit.h.
+
+ These routines must handle speed setting, parity, flow control, file i/o,
+ and similar items without the kermit() routine knowing anything about it.
+ If parity is in effect, these routines must add it to outbound characters
+ and strip it from inbound characters.
+*/
+#include <stdio.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <errno.h>
+#ifndef O_WRONLY
+#include <sys/file.h>
+#ifdef X_OK
+#undef X_OK
+#endif /* X_OK */
+#endif /* O_WRONLY */
+
+#include "cdefs.h"
+#include "debug.h"
+#include "platform.h"
+#include "kermit.h"
+
+UCHAR o_buf[OBUFLEN+8]; /* File output buffer */
+UCHAR i_buf[IBUFLEN+8]; /* File output buffer */
+
+/*
+ In this example, the output file is unbuffered to ensure that every
+ output byte is commited. The input file, however, is buffered for speed.
+ This is just one of many possible implmentation choices, invisible to the
+ Kermit protocol module.
+*/
+static int ttyfd, ofile = -1; /* File descriptors */
+static FILE * ifile = (FILE *)0; /* and pointers */
+
+/* Debugging */
+
+#ifdef DEBUG
+static FILE * dp = (FILE *)0; /* Debug log */
+static int xdebug = 0; /* Debugging on/off */
+
+void
+dodebug(int fc, UCHAR * label, UCHAR * sval, long nval) {
+
+ if (fc != DB_OPN && !xdebug)
+ return;
+ if (!label)
+ label = "";
+
+ switch (fc) { /* Function code */
+ case DB_OPN: /* Open debug log */
+ if (dp) fclose(dp);
+ if (!*label) label = "debug.log";
+ dp = fopen(label,"w");
+ if (!dp) {
+ dp = stderr;
+ } else {
+ setbuf(dp,(char *)0);
+ }
+ xdebug = 1;
+ fprintf(dp,"DEBUG LOG OPEN\n");
+ return;
+ case DB_MSG: /* Write a message */
+ if (dp) fprintf(dp,"%s\n",label);
+ return;
+ case DB_CHR: /* Write label and character */
+ if (dp) fprintf(dp,"%s=[%c]\n",label,(char)nval);
+ return;
+ case DB_PKT: /* Log a packet */
+ /* (fill in later, fall thru for now...) */
+ case DB_LOG: /* Write label and string or number */
+ if (sval && dp)
+ fprintf(dp,"%s[%s]\n",label,sval);
+ else
+ fprintf(dp,"%s=%ld\n",label,nval);
+ return;
+ case DB_CLS: /* Close debug log */
+ if (dp) {
+ fclose(dp);
+ dp = (FILE *)0;
+ }
+ xdebug = 0;
+ }
+}
+#endif /* DEBUG */
+
+/* D E V O P E N -- Open communications device */
+/*
+
+ Call with: string pointer to device name. This routine should get the
+ current device settings and save them so devclose() can restore them.
+ It should open the device. If the device is a serial port, devopen()
+ set the speed, stop bits, flow control, etc.
+ Returns: 0 on failure, 1 on success.
+*/
+int
+devopen(char *device) {
+ ttyfd = 0;
+ return(1);
+}
+
+/* P K T M O D E -- Put communications device into or out of packet mode */
+/*
+ Call with: 0 to put in normal (cooked) mode, 1 to put in packet (raw) mode.
+ For a "dumb i/o device" like an i/o port that does not have a login attached
+ to it, this routine can usually be a no-op.
+ Returns: 0 on failure, 1 on success.
+*/
+int
+pktmode(short on) {
+ if (ttyfd < 0) /* Device must be open */
+ return(0);
+ system(on ? "stty raw -echo" : "stty sane"); /* Crude but effective */
+ return(1);
+}
+
+
+/* D E V S E T T I N G S */
+
+int
+devsettings(char * s) {
+ /* Get current device settings, save them for devrestore() */
+ /* Parse string s, do whatever it says, e.g. "9600;8N1" */
+ if (!pktmode(ON)) /* And put device in packet mode */
+ return(0);
+ return(1);
+}
+
+/* D E V R E S T O R E */
+
+int
+devrestore(void) {
+ /* Put device back as we found it */
+ pktmode(OFF);
+ return(1);
+}
+
+
+/* D E V C L O S E -- Closes the current open communications device */
+/*
+ Call with: nothing
+ Closes the device and puts it back the way it was found by devopen().
+ Returns: 0 on failure, 1 on success.
+*/
+int
+devclose(void) {
+ ttyfd = -1;
+ return(1);
+}
+
+/* I N C H K -- Check if input waiting */
+
+/*
+ Check if input is waiting to be read, needed for sliding windows. This
+ sample version simply looks in the stdin buffer (which is not portable
+ even among different Unixes). If your platform does not provide a way to
+ look at the device input buffer without blocking and without actually
+ reading from it, make this routine return -1. On success, returns the
+ numbers of characters waiting to be read, i.e. that can be safely read
+ without blocking.
+*/
+int
+inchk(struct k_data * k) {
+#ifdef _IO_file_flags /* Linux */
+ if (ttyfd < 0) /* Device must be open */
+ return(0);
+ return((int) ((stdin->_IO_read_end) - (stdin->_IO_read_ptr)));
+#else
+#ifdef AIX /* AIX */
+ if (ttyfd < 0)
+ return(0);
+ return(stdin->_cnt);
+#else
+#ifdef SunOS /* Solaris and SunOS */
+ if (ttyfd < 0)
+ return(0);
+ return(stdin->_cnt);
+#else
+#ifdef HPUX /* HPUX */
+ if (ttyfd < 0)
+ return(0);
+ return(stdin->__cnt);
+#else
+ return(-1);
+#endif /* HPUX */
+#endif /* SunOS */
+#endif /* AIX */
+#endif /* _IO_file_flags */
+}
+
+/* R E A D P K T -- Read a Kermit packet from the communications device */
+/*
+ Call with:
+ k - Kermit struct pointer
+ p - pointer to read buffer
+ len - length of read buffer
+
+ When reading a packet, this function looks for start of Kermit packet
+ (k->r_soh), then reads everything between it and the end of the packet
+ (k->r_eom) into the indicated buffer. Returns the number of bytes read, or:
+ 0 - timeout or other possibly correctable error;
+ -1 - fatal error, such as loss of connection, or no buffer to read into.
+*/
+
+int
+readpkt(struct k_data * k, UCHAR *p, int len, int fc) {
+ int x, n, max;
+ short flag;
+ UCHAR c;
+/*
+ Timeout not implemented in this sample.
+ It should not be needed. All non-embedded Kermits that are capable of
+ making connections are also capable of timing out, and only one Kermit
+ needs to time out. NOTE: This simple example waits for SOH and then
+ reads everything up to the negotiated packet terminator. A more robust
+ version might be driven by the value of the packet-length field.
+*/
+#ifdef DEBUG
+ char * p2;
+#endif /* DEBUG */
+
+#ifdef F_CTRLC
+ short ccn;
+ ccn = 0;
+#endif /* F_CTRLC */
+
+ if (ttyfd < 0 || !p) { /* Device not open or no buffer */
+ debug(DB_MSG,"readpkt FAIL",0,0);
+ return(-1);
+ }
+ flag = n = 0; /* Init local variables */
+
+#ifdef DEBUG
+ p2 = p;
+#endif /* DEBUG */
+
+ while (1) {
+ x = getchar(); /* Replace this with real i/o */
+ c = (k->parity) ? x & 0x7f : x & 0xff; /* Strip parity */
+
+#ifdef F_CTRLC
+ /* In remote mode only: three consecutive ^C's to quit */
+ if (k->remote && c == (UCHAR) 3) {
+ if (++ccn > 2) {
+ debug(DB_MSG,"readpkt ^C^C^C",0,0);
+ return(-1);
+ }
+ } else {
+ ccn = 0;
+ }
+#endif /* F_CTRLC */
+
+ if (!flag && c != k->r_soh) /* No start of packet yet */
+ continue; /* so discard these bytes. */
+ if (c == k->r_soh) { /* Start of packet */
+ flag = 1; /* Remember */
+ continue; /* But discard. */
+ } else if (c == k->r_eom /* Packet terminator */
+ || c == '\012' /* 1.3: For HyperTerminal */
+ ) {
+#ifdef DEBUG
+ *p = NUL; /* Terminate for printing */
+ debug(DB_PKT,"RPKT",p2,n);
+#endif /* DEBUG */
+ return(n);
+ } else { /* Contents of packet */
+ if (n++ > k->r_maxlen) /* Check length */
+ return(0);
+ else
+ *p++ = x & 0xff;
+ }
+ }
+ debug(DB_MSG,"READPKT FAIL (end)",0,0);
+ return(-1);
+}
+
+/* T X _ D A T A -- Writes n bytes of data to communication device. */
+/*
+ Call with:
+ k = pointer to Kermit struct.
+ p = pointer to data to transmit.
+ n = length.
+ Returns:
+ X_OK on success.
+ X_ERROR on failure to write - i/o error.
+*/
+int
+tx_data(struct k_data * k, UCHAR *p, int n) {
+ int x;
+ int max;
+
+ max = 10; /* Loop breaker */
+
+ while (n > 0) { /* Keep trying till done */
+ x = write(ttyfd,p,n);
+ debug(DB_MSG,"tx_data write",0,x);
+ if (x < 0 || --max < 1) /* Errors are fatal */
+ return(X_ERROR);
+ n -= x;
+ p += x;
+ }
+ return(X_OK); /* Success */
+}
+
+/* O P E N F I L E -- Open output file */
+/*
+ Call with:
+ Pointer to filename.
+ Size in bytes.
+ Creation date in format yyyymmdd hh:mm:ss, e.g. 19950208 14:00:00
+ Mode: 1 = read, 2 = create, 3 = append.
+ Returns:
+ X_OK on success.
+ X_ERROR on failure, including rejection based on name, size, or date.
+*/
+int
+openfile(struct k_data * k, UCHAR * s, int mode) {
+
+ switch (mode) {
+ case 1: /* Read */
+ if (!(ifile = fopen(s,"r"))) {
+ debug(DB_LOG,"openfile read error",s,0);
+ return(X_ERROR);
+ }
+ k->s_first = 1; /* Set up for getkpt */
+ k->zinbuf[0] = '\0'; /* Initialize buffer */
+ k->zinptr = k->zinbuf; /* Set up buffer pointer */
+ k->zincnt = 0; /* and count */
+ debug(DB_LOG,"openfile read ok",s,0);
+ return(X_OK);
+
+ case 2: /* Write (create) */
+ ofile = creat(s,0644);
+ if (ofile < 0) {
+ debug(DB_LOG,"openfile write error",s,0);
+ return(X_ERROR);
+ }
+ debug(DB_LOG,"openfile write ok",s,0);
+ return(X_OK);
+
+#ifdef COMMENT
+ case 3: /* Append (not used) */
+ ofile = open(s,O_WRONLY|O_APPEND);
+ if (ofile < 0) {
+ debug(DB_LOG,"openfile append error",s,0);
+ return(X_ERROR);
+ }
+ debug(DB_LOG,"openfile append ok",s,0);
+ return(X_OK);
+#endif /* COMMENT */
+
+ default:
+ return(X_ERROR);
+ }
+}
+
+/* F I L E I N F O -- Get info about existing file */
+/*
+ Call with:
+ Pointer to filename
+ Pointer to buffer for date-time string
+ Length of date-time string buffer (must be at least 18 bytes)
+ Pointer to int file type:
+ 0: Prevailing type is text.
+ 1: Prevailing type is binary.
+ Transfer mode (0 = auto, 1 = manual):
+ 0: Figure out whether file is text or binary and return type.
+ 1: (nonzero) Don't try to figure out file type.
+ Returns:
+ X_ERROR on failure.
+ 0L or greater on success == file length.
+ Date-time string set to yyyymmdd hh:mm:ss modtime of file.
+ If date can't be determined, first byte of buffer is set to NUL.
+ Type set to 0 (text) or 1 (binary) if mode == 0.
+*/
+#ifdef F_SCAN
+#define SCANBUF 1024
+#define SCANSIZ 49152
+#endif /* F_SCAN */
+
+ULONG
+fileinfo(struct k_data * k,
+ UCHAR * filename, UCHAR * buf, int buflen, short * type, short mode) {
+ struct stat statbuf;
+ struct tm * timestamp, * localtime();
+
+#ifdef F_SCAN
+ FILE * fp; /* File scan pointer */
+ char inbuf[SCANBUF]; /* and buffer */
+#endif /* F_SCAN */
+
+ if (!buf)
+ return(X_ERROR);
+ buf[0] = '\0';
+ if (buflen < 18)
+ return(X_ERROR);
+ if (stat(filename,&statbuf) < 0)
+ return(X_ERROR);
+ timestamp = localtime(&(statbuf.st_mtime));
+ sprintf(buf,"%04d%02d%02d %02d:%02d:%02d",
+ timestamp->tm_year + 1900,
+ timestamp->tm_mon + 1,
+ timestamp->tm_mday,
+ timestamp->tm_hour,
+ timestamp->tm_min,
+ timestamp->tm_sec
+ );
+#ifdef F_SCAN
+/*
+ Here we determine if the file is text or binary if the transfer mode is
+ not forced. This is an extremely crude sample, which diagnoses any file
+ that contains a control character other than HT, LF, FF, or CR as binary.
+ A more thorough content analysis can be done that accounts for various
+ character sets as well as various forms of Unicode (UTF-8, UTF-16, etc).
+ Or the diagnosis could be based wholly or in part on the filename.
+ etc etc. Or the implementation could skip this entirely by not defining
+ F_SCAN and/or by always calling this routine with type set to -1.
+*/
+ if (!mode) { /* File type determination requested */
+ int isbinary = 1;
+ fp = fopen(filename,"r"); /* Open the file for scanning */
+ if (fp) {
+ int n = 0, count = 0;
+ char c, * p;
+
+ debug(DB_LOG,"fileinfo scan ",filename,0);
+
+ isbinary = 0;
+ while (count < SCANSIZ && !isbinary) { /* Scan this much */
+ n = fread(inbuf,1,SCANBUF,fp);
+ if (n == EOF || n == 0)
+ break;
+ count += n;
+ p = inbuf;
+ while (n--) {
+ c = *p++;
+ if (c < 32 || c == 127) {
+ if (c != 9 && /* Tab */
+ c != 10 && /* LF */
+ c != 12 && /* FF */
+ c != 13) { /* CR */
+ isbinary = 1;
+ debug(DB_MSG,"fileinfo BINARY",0,0);
+ break;
+ }
+ }
+ }
+ }
+ fclose(fp);
+ *type = isbinary;
+ }
+ }
+#endif /* F_SCAN */
+
+ return((ULONG)(statbuf.st_size));
+}
+
+
+/* R E A D F I L E -- Read data from a file */
+
+int
+readfile(struct k_data * k) {
+ if (!k->zinptr) {
+#ifdef DEBUG
+ fprintf(dp,"readfile ZINPTR NOT SET\n");
+#endif /* DEBUG */
+ return(X_ERROR);
+ }
+ if (k->zincnt < 1) { /* Nothing in buffer - must refill */
+ if (k->binary) { /* Binary - just read raw buffers */
+ k->dummy = 0;
+ k->zincnt = fread(k->zinbuf, 1, k->zinlen, ifile);
+ debug(DB_LOG,"readfile binary ok zincnt",0,k->zincnt);
+
+ } else { /* Text mode needs LF/CRLF handling */
+ int c; /* Current character */
+ for (k->zincnt = 0; (k->zincnt < (k->zinlen - 2)); (k->zincnt)++) {
+ if ((c = getc(ifile)) == EOF)
+ break;
+ if (c == '\n') /* Have newline? */
+ k->zinbuf[(k->zincnt)++] = '\r'; /* Insert CR */
+ k->zinbuf[k->zincnt] = c;
+ }
+#ifdef DEBUG
+ k->zinbuf[k->zincnt] = '\0';
+ debug(DB_LOG,"readfile text ok zincnt",0,k->zincnt);
+#endif /* DEBUG */
+ }
+ k->zinbuf[k->zincnt] = '\0'; /* Terminate. */
+ if (k->zincnt == 0) /* Check for EOF */
+ return(-1);
+ k->zinptr = k->zinbuf; /* Not EOF - reset pointer */
+ }
+ (k->zincnt)--; /* Return first byte. */
+
+ debug(DB_LOG,"readfile exit zincnt",0,k->zincnt);
+ debug(DB_LOG,"readfile exit zinptr",0,k->zinptr);
+ return(*(k->zinptr)++ & 0xff);
+}
+
+
+/* W R I T E F I L E -- Write data to file */
+/*
+ Call with:
+ Kermit struct
+ String pointer
+ Length
+ Returns:
+ X_OK on success
+ X_ERROR on failure, such as i/o error, space used up, etc
+*/
+int
+writefile(struct k_data * k, UCHAR * s, int n) {
+ int rc;
+ rc = X_OK;
+
+ debug(DB_LOG,"writefile binary",0,k->binary);
+
+ if (k->binary) { /* Binary mode, just write it */
+ if (write(ofile,s,n) != n)
+ rc = X_ERROR;
+ } else { /* Text mode, skip CRs */
+ UCHAR * p, * q;
+ int i;
+ q = s;
+
+ while (1) {
+ for (p = q, i = 0; ((*p) && (*p != (UCHAR)13)); p++, i++) ;
+ if (i > 0)
+ if (write(ofile,q,i) != i)
+ rc = X_ERROR;
+ if (!*p) break;
+ q = p+1;
+ }
+ }
+ return(rc);
+}
+
+/* C L O S E F I L E -- Close output file */
+/*
+ Mode = 1 for input file, mode = 2 or 3 for output file.
+
+ For output files, the character c is the character (if any) from the Z
+ packet data field. If it is D, it means the file transfer was canceled
+ in midstream by the sender, and the file is therefore incomplete. This
+ routine should check for that and decide what to do. It should be
+ harmless to call this routine for a file that that is not open.
+*/
+int
+closefile(struct k_data * k, UCHAR c, int mode) {
+ int rc = X_OK; /* Return code */
+
+ switch (mode) {
+ case 1: /* Closing input file */
+ if (!ifile) /* If not not open */
+ break; /* do nothing but succeed */
+ debug(DB_LOG,"closefile (input)",k->filename,0);
+ if (fclose(ifile) < 0)
+ rc = X_ERROR;
+ break;
+ case 2: /* Closing output file */
+ case 3:
+ if (ofile < 0) /* If not open */
+ break; /* do nothing but succeed */
+ debug(DB_LOG,"closefile (output) name",k->filename,0);
+ debug(DB_LOG,"closefile (output) keep",0,k->ikeep);
+ if (close(ofile) < 0) { /* Try to close */
+ rc = X_ERROR;
+ } else if ((k->ikeep == 0) && /* Don't keep incomplete files */
+ (c == 'D')) { /* This file was incomplete */
+ if (k->filename) {
+ debug(DB_LOG,"deleting incomplete",k->filename,0);
+ unlink(k->filename); /* Delete it. */
+ }
+ }
+ break;
+ default:
+ rc = X_ERROR;
+ }
+ return(rc);
+}
+
+#ifdef DEBUG
+int xerror() {
+ unsigned int x;
+ extern int errorrate; /* Fix this - NO EXTERNS */
+ if (!errorrate)
+ return(0);
+ x = rand() % 100; /* Fix this - NO C LIBRARY */
+ debug(DB_LOG,"RANDOM",0,x);
+ debug(DB_LOG,"ERROR",0,(x < errorrate));
+ return(x < errorrate);
+}
+#endif /* DEBUG */