emigh@ecsvax.UUCP (12/21/83)
Following is the latest version of UC.C (UNIX(tm) to CP/M file transfer
shell) which corrects a minor bug. To extract the program, delete this
header and run through the shell. The program was written by Rick Conn
(...!ucbvax!rconn@brl). The manual page previously posted is still valid.
Ted H. Emigh
mcnc!ecsvax!emigh
------------------------------------------------------------------------
: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
all=TRUE
fi
/bin/echo 'Extracting uc.c'
sed 's/^X//' <<'//go.sysin dd *' >uc.c
X/*
* UC - a UNIX-to-CP/M file transfer shell
* by Richard Conn
*
* UC is based on ideas from UMODEM 3.5, where UMODEM was originally written
* by Lauren Weinstein, and was mutated by Richard Conn, Bennett Marks,
* Michael Rubenstein, Ben Goldfarb, David Hinnant, and Lauren Weinstein
* into version 3.5. UC is a totally new design, including many features
* of similar function but different implementation and adding many new
* features and both a menu-driven and command-completion user interface.
*
* UC is a rather complete rewrite of the UMODEM program, with emphasis
* on implementation as a "pseudo-shell".
*
*/
#define versmaj 1 /* Major Version */
#define versmin 4 /* Minor Version */
X/* Basics */
#define FALSE 0
#define TRUE ~FALSE
X/* ASCII Characters */
#define SOH 001
#define STX 002
#define ETX 003
#define EOT 004
#define ENQ 005
#define ACK 006
#define LF 012
#define CR 015
#define NAK 025
#define SYN 026
#define CAN 030
#define CTRLZ 032
#define ESC 033
X/* UC Constants */
#define TO -1 /* Timeout Flag */
#define ERRMAX 10 /* Max errors tolerated */
#define BLOCKSZ 128 /* Size of transmission block */
#define CREATE 0644 /* Mode for New Files */
#define LOGFILE "uc.log" /* Log File */
#define CFGFILE ".ucsetup" /* Configuration File */
#define DBGFILE "uc.debug" /* Debug File */
X/* UC Defaults */
#define defarpa FALSE /* Not Using ARPA Net */
#define defmenu FALSE /* Menu */
#define defftp 3 /* FTP */
#define deflog TRUE /* Log to Disk? */
#define defbit7 FALSE /* 7-Bit Transfer? */
X/* Library Utilities */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sgtty.h>
#include <signal.h>
#include <ctype.h>
X/* Configuration Structure */
struct environ {
char filetype; /* text or binary */
int ftp; /* File Transfer Protocol */
int logflg; /* log error summary to file */
int dbgflg; /* debug output flag */
FILE *logfd; /* file descriptor for log file */
FILE *dbgfd; /* file descriptor for debug file */
int bit7; /* allow only 7-bit transfers */
int menu; /* menu for command mode */
int arpa; /* negotiate binary on ARPA Net */
}
X/* ARPA Net Constants */
X/* The following constants are used to communicate with the ARPA
* Net SERVER TELNET and TAC programs. These constants are defined
* as follows:
* IAC <-- Is A Command; indicates that
* a command follows
* WILL/WONT <-- Command issued to SERVER TELNET
* (Host); WILL issues command
* and WONT issues negative of
* the command
* DO/DONT <-- Command issued to TAC; DO issues
* command and DONT issues
* negative of the command
* TRBIN <-- Transmit Binary Command
* Examples:
* IAC WILL TRBIN <-- Host is configured to transmit Binary
* IAC WONT TRBIN <-- Host is configured NOT to transmit binary
* IAC DO TRBIN <-- TIP is configured to transmit Binary
* IAC DONT TRBIN <-- TIP is configured NOT to transmit binary
*/
#define IAC 0377 /* Is A Command */
#define DO 0375 /* Command to TAC */
#define DONT 0376 /* Negative of Command to TAC */
#define WILL 0373 /* Command to SERVER TELNET (Host) */
#define WONT 0374 /* Negative of Command to SERVER TELNET */
#define TRBIN 0 /* Transmit Binary Command */
main (argc, argv)
int argc;
char *argv[];
{
int sendflg, recvflg, statflg, cmndflg; /* major functions */
int crckflg; /* major function */
struct environ genv; /* global environ */
FILE *fopen(); /* forward ref */
int index; /* index for arg parsing loop */
char opt; /* current option character */
char *getenv(); /* getenv function defn */
char logfile[50]; /* space for name of log file */
char cfgfile[50]; /* space for name of configuration file */
char dbgfile[50]; /* space for name of debug file */
/* Print Banner */
printf("UC Version %d.%d - UNIX-to-CP/M File Transfer Tool\n",
versmaj, versmin);
/* Check for Help Request */
if (argc == 1) {
help();
exit(0);
}
/* Determine Name of Log File */
strcat (logfile, getenv("HOME")); /* Name of Home Dir */
strcat (logfile, "/"); /* Separator */
strcat (logfile, LOGFILE); /* Name of Log File */
/* Determine Name of Configuration File */
strcat (cfgfile, getenv("HOME")); /* Name of Home Dir */
strcat (cfgfile, "/"); /* Separator */
strcat (cfgfile, CFGFILE); /* Name of Configuration File */
/* Determine Name of Debug File */
strcat (dbgfile, getenv("HOME")); /* Name of Home Dir */
strcat (dbgfile, "/"); /* Separator */
strcat (dbgfile, DBGFILE); /* Name of Debug File */
/* Set Defaults for ARPA Net, Menu, FTP, Logging, and 7/8-Bit Xfer */
genv.logflg = deflog;
genv.dbgflg = FALSE; /* Debug Off */
genv.bit7 = defbit7;
genv.ftp = defftp;
genv.menu = defmenu;
genv.arpa = defarpa;
getdef (cfgfile, &genv); /* Get Defs from File */
/* Init for Option Parsing */
sendflg = FALSE;
recvflg = FALSE;
statflg = FALSE;
crckflg = FALSE;
cmndflg = FALSE;
/* Process Options */
index = 0;
while ((opt = argv[1][index++]) != '\0')
switch (opt) {
case '-' : /* skip dash */
break;
case '1' :
genv.ftp = 1; /* select FTP 1 */
break;
case '3' :
genv.ftp = 3; /* select FTP 3 */
break;
case '7' :
genv.bit7 = TRUE; /* set 7 bits */
break;
case '8' :
genv.bit7 = FALSE; /* set 8 bits */
break;
case 'A' :
case 'a' :
genv.arpa = TRUE; /* set ARPA Net */
break;
case 'B' :
case 'b' :
genv.filetype = 'b'; /* set binary type */
break;
case 'C' :
crckflg = TRUE; /* set crc check mode */
genv.filetype = 'b'; /* binary */
break;
case 'c' :
crckflg = TRUE; /* set crc check mode */
genv.filetype = 't'; /* text */
break;
case 'D' :
case 'd' :
genv.dbgflg = ~genv.dbgflg; /* flip flag */
break;
case 'F' :
case 'f' :
statflg = TRUE; /* set file stat mode */
break;
case 'L' :
case 'l' :
genv.logflg = ~genv.logflg; /* comp log */
break;
case 'R' :
recvflg = TRUE; /* set file recv mode */
genv.filetype = 'b';
break;
case 'r' :
recvflg = TRUE; /* set file recv mode */
genv.filetype = 't';
break;
case 'S' :
sendflg = TRUE; /* set file send mode */
genv.filetype = 'b';
break;
case 's' :
sendflg = TRUE; /* set file send mode */
genv.filetype = 't';
break;
case 'T' :
case 't' :
genv.filetype = 't'; /* set text file type */
break;
case 'Z' :
case 'z' :
cmndflg = TRUE; /* set command mode */
break;
default :
printf("Invalid Option %c\n", opt);
break;
}
/* Open Debug File if Requested */
if (genv.dbgflg) {
genv.dbgfd = fopen(dbgfile, "w");
if (genv.dbgfd == NULL) {
printf("Can't Open Debug File\n");
exit(0);
}
}
/* Open Log File if Needed */
if ((sendflg || recvflg || cmndflg) && genv.logflg) {
genv.logfd = fopen(logfile, "w");
if (genv.logfd == NULL) {
printf("Can't Open Log File\n");
exit(0);
}
}
/* Select and Execute Major Mode */
if (cmndflg) { /* Command Mode */
command(&genv);
if (genv.logflg) fclose(genv.logfd);
if (genv.dbgflg) fclose(genv.dbgfd);
exit(0);
}
if (statflg) { /* File Status Display */
if (argc < 3) {
printf("File Name NOT Given\n");
exit(0);
}
genv.logflg = FALSE; /* no logging right now */
fstat(&genv,argv[2]);
exit(0);
}
if (crckflg) { /* CRC Check */
if (argc < 3) {
printf("File Name NOT Given\n");
exit(0);
}
if (genv.filetype == 't') crct(argv[2]);
else crcb(argv[2]);
exit(0);
}
if (sendflg) { /* Send File */
if (argc < 3) {
printf("File Name NOT Given\n");
exit(0);
}
send(&genv,argv[2]);
if (genv.logflg) fclose(genv.logfd);
if (genv.dbgflg) fclose(genv.dbgfd);
exit(0);
}
if (recvflg) { /* Receive File */
if (argc < 3) {
printf("File Name NOT Given\n");
exit(0);
}
recv(&genv,argv[2]);
if (genv.logflg) fclose(genv.logfd);
if (genv.dbgflg) fclose(genv.dbgfd);
exit(0);
}
printf("Major Mode NOT Selected\n");
help();
exit(0);
}
X/* Get Defaults from User's Configuration File, if Any */
getdef (filename, env)
char *filename;
struct environ *env;
{
FILE *fd; /* File Descriptor */
FILE *fopen(); /* fopen Function */
int c; /* Dummy Input Char */
/* Open File */
if ((fd = fopen (filename, "r")) == NULL) return; /* no file */
printf("UC Configuration File %s\n", filename);
/* Read Loop */
while ((c = getc(fd)) != EOF)
switch (c) {
case '-' :
c = getc(fd); /* get next char */
switch (c) {
case 'A' :
case 'a' :
env->arpa = FALSE;
break;
case 'L' :
case 'l' :
env->logflg = FALSE;
break;
case 'M' :
case 'm' :
env->menu = FALSE;
break;
default :
ungetc(fd,c); /* put back */
break;
}
break;
case '!' : /* Comment */
do {
c = getc(fd); /* Flush Comment */
}
while (c != LF && c != EOF);
if (c == EOF) ungetc(fd,c);
break;
case '1' :
env->ftp = 1;
break;
case '3' :
env->ftp = 3;
break;
case '7' :
env->bit7 = TRUE;
break;
case '8' :
env->bit7 = FALSE;
break;
case 'A' :
case 'a' :
env->arpa = TRUE;
case 'L' :
case 'l' :
env->logflg = TRUE;
break;
case 'M' :
case 'm' :
env->menu = TRUE;
break;
default :
break;
}
/* Close File */
fclose(fd);
}
X/* Print Help */
help()
{
printf("Usage: uc c[o] [filename]\n");
printf("\n");
printf("where 'c' MUST be One of the Following Commands --\n");
printf("\tC -- CRC Check on Binary File (filename required)\n");
printf("\tc -- CRC Check on Text File (filename required)\n");
printf("\td or D -- Debug Output (to UC.DEBUG)\n");
printf("\tf or F -- File Status (filename required)\n");
printf("\tR -- Receive Binary File (filename required)\n");
printf("\tr -- Receive Text File (filename required)\n");
printf("\tS -- Send Binary File (filename required)\n");
printf("\ts -- Send Text File (filename required)\n");
printf("\tz or Z -- Enter Command Mode (filename NOT required)\n");
printf("\n");
printf("and Additional Options 'o' Include --\n");
printf("\t1 -- Select File Transfer Protocol 1\n");
printf("\t7 -- Select 7-Bit Transfer\n");
printf("\ta or A -- Enable ARPA Net Communication\n");
printf("\tb or B -- Override 'c', 's', or 'r' to Binary\n");
printf("\tl or L -- Turn Off Log Entries\n");
printf("\tt or T -- Override 'C', 'S', or 'R' to Text\n");
printf("\n");
printf("Examples:\n");
printf("\tuc S myfile -or- uc sb myfile <-- Send Binary File\n");
printf("\tuc s7l myfile <-- Send Text File with 7 Bits and No Log\n");
}
X/* Command Mode */
command(env)
struct environ *env;
{
int charx(), chelp();
int running;
int tlogflg;
char c, uline[200], cline[200];
printf("UC Command Mode -- Type 'h' for Help\n");
running = TRUE;
while (running) {
if (env->menu) chelp();
printf("UC Command? ");
switch (c = charx()) {
case CR :
printf("\n");
break;
case '1' :
env->ftp = 1;
printf("FTP 1\n");
break;
case '3' :
env->ftp = 3;
printf("FTP 3\n");
break;
case '7' :
env->bit7 = TRUE;
printf("7-Bit Transmission\n");
break;
case '8' :
env->bit7 = FALSE;
printf("8-Bit Transmission\n");
break;
case 'A' :
case 'a' :
env->arpa = ~env->arpa;
printf("ARPA Net Communication %s\n",
env->arpa ? "Enabled" : "Disabled");
break;
case 'C' :
printf("CRC of Binary File (file name) ");
gets(uline);
if (uline[0]) crcb(uline);
break;
case 'c' :
printf("CRC of Text File (file name) ");
gets(uline);
if (uline[0]) crct(uline);
break;
case 'D' :
case 'd' :
printf("Directory of (dir or file spec) ");
gets(uline);
cline[0] = '\0';
strcat (cline, "dir ");
strcat (cline, uline);
system(cline);
break;
case 'E' :
case 'e' :
printf("Transmission Environment\n");
printf("\tFile Transfer Protocol %c\n",
env->ftp==1 ? '1' : '3');
printf("\t%c-Bit Transfer\n",
env->bit7 ? '7' : '8');
printf("\tARPA Net Communication %s\n",
env->arpa ? "Enabled" : "Disabled");
break;
case 'F' :
case 'f' :
printf("File Status of (file name) ");
gets(uline);
tlogflg = env->logflg;
env->logflg = FALSE;
if (uline[0]) fstat(env,uline);
env->logflg = tlogflg;
break;
case '?' :
case '/' :
case 'H' :
case 'h' :
printf("\n");
chelp();
break;
case 'L' :
case 'l' :
printf("Login (directory) ");
gets(uline);
if (uline[0]) chdir(uline);
system("pwd");
break;
case 'M' :
case 'm' :
env->menu = ~env->menu; /* toggle menu */
printf("Menu %s\n", env->menu ? "ON" : "OFF");
break;
case 'R' :
env->filetype = 'b';
env->bit7 = FALSE;
printf("Receive Binary File (file name) ");
gets(uline);
if (uline[0]) recv(env,uline);
printf("\n");
break;
case 'r' :
env->filetype = 't';
printf("Receive Text File (file name) ");
gets(uline);
if (uline[0]) recv(env,uline);
printf("\n");
break;
case 'S' :
env->filetype = 'b';
env->bit7 = FALSE;
printf("Send Binary File (file name) ");
gets(uline);
if (uline[0]) send(env,uline);
printf("\n");
break;
case 's' :
env->filetype = 't';
printf("Send Text File (file name) ");
gets(uline);
if (uline[0]) send(env,uline);
printf("\n");
break;
case 'X':
case 'x' :
running = FALSE;
printf("Exit\n");
break;
case 'Z' :
case 'z' :
printf("UNIX Command Line ");
printf("(command line) ");
gets(uline);
if (uline[0]) system(uline);
printf("\n");
break;
default :
printf("Invalid Command %c -- Type H for Help\n", c);
break;
}
}
}
X/* print help for command mode */
chelp()
{
printf("\t\t\t\tUC Command Summary\n");
printf("\n");
printf("\t------- Major Function ------- -- File Xfer Options --\n");
printf("\t c <file> CRC Value of File 1 or 3 Select FTP\n");
printf("\t r <file> Receive File 7 or 8 Select Bits\n");
printf("\t s <file> Send File a or A Toggle ARPA Net\n");
printf("\t e or E Display Environ\n");
printf("\n");
printf("\t------- UNIX Function ------- -------- Notes --------\n");
printf("\td <dir/file> Display Directory Major Fct - if caps(C),\n");
printf("\tf <file> File Size Info binary file; if not(c),\n");
printf("\tl <dir> Log Into Dir text file\n");
printf("\tm Toggle Menu\n");
printf("\tx Exit to UNIX UNIX Fct - either case\n");
printf("\tz UNIX Command\n");
printf("\n");
}
X/* Send File */
send(env,filename)
struct environ *env;
char *filename;
{
FILE *fd, *fopen();
int blocknum; /* Current Block Number */
int nlflg; /* New Line for File Convert */
int sending; /* Xmit In-Progress Flag */
int tries; /* Attempt Count */
int bufctr; /* Counter for Buffer Build */
int bitmask; /* 7/8 Bit Mask */
int c; /* Temp Char */
int rcode; /* Return Code */
char buf[BLOCKSZ]; /* Buffer for Transfer */
/* Print Banner */
printf("UC Sending %s File: %s\n",
(env->filetype == 't') ? "Text" : "Binary",
filename);
if (env->logflg) fprintf(env->logfd,
"\nUC Sending %s File: %s\n",
(env->filetype == 't') ? "Text" : "Binary",
filename);
if (env->dbgflg) fprintf(env->dbgfd,
"\nUC Sending %s File: %s\n",
(env->filetype == 't') ? "Text" : "Binary",
filename);
/* Open File for Input and Print Opening Messages */
if ((fd = fopen(filename, "r")) == 0) {
printf("Can`t Open File %s for Send\n", filename);
if (env->logflg) fprintf(env->logfd,
"Can't Open File %s for Send\n", filename);
if (env->dbgflg) fprintf(env->dbgfd,
"Can't Open File %s for Send\n", filename);
return;
}
fstat(env,filename); /* Print File Status Info */
printf("FTP %c, %c-Bit Transfer Selected\n",
(env->ftp == 1) ? '1' : '3',
env->bit7 ? '7' : '8');
if (env->logflg) fprintf(env->logfd,
"FTP %c, %c-Bit Transfer Selected\n",
(env->ftp == 1) ? '1' : '3',
env->bit7 ? '7' : '8');
if (env->dbgflg) fprintf(env->dbgfd,
"FTP %c, %c-Bit Transfer Selected\n",
(env->ftp == 1) ? '1' : '3',
env->bit7 ? '7' : '8');
printf("Ready to Send File\n");
binary(TRUE,TRUE); /* Open Binary Communications */
if (env->arpa) setarpa(); /* Binary on ARPA Net? */
if (env->bit7) bitmask = 0x7f;
else bitmask = 0xff;
/* Init Parameters */
blocknum = 1;
nlflg = FALSE;
sending = TRUE;
/* Synchronize */
tries = 0;
if (env->ftp == 1) {
sendbyte(SYN);
while (recvbyte(5,bitmask) != ACK) {
if (++tries > ERRMAX) {
printf("Remote System Not Responding\n");
if (env->logflg) fprintf(env->logfd,
"Remote System Not Responding\n");
if (env->dbgflg) fprintf(env->dbgfd,
"Remote System Not Responding\n");
return;
}
sendbyte(SYN);
}
}
else {
while (recvbyte(30,bitmask) != NAK)
if (++tries > ERRMAX) {
printf("Remote System Not Responding\n");
if (env->logflg) fprintf(env->logfd,
"Remote System Not Responding\n");
if (env->dbgflg) fprintf(env->dbgfd,
"Remote System Not Responding\n");
return;
}
}
/* Main Transmission Loop */
while (sending) {
/* Build Next Block into buf */
for (bufctr = 0; bufctr < BLOCKSZ;) {
if (nlflg) { /* New Line */
buf[bufctr++] = LF; /* Store LF */
nlflg = FALSE;
}
if (bufctr == BLOCKSZ) break; /* Leave for Loop */
c = getc(fd); /* Get Next Byte from File */
if (c == EOF) {
sending = FALSE; /* Done */
if (!bufctr) /* Avoid Extra Block */
break;
if (env->filetype == 't')
for (;bufctr < BLOCKSZ; bufctr++)
buf[bufctr] = CTRLZ;
continue; /* Exit for Loop */
}
if (c == LF && env->filetype == 't') { /* NL? */
buf[bufctr++] = CR; /* Insert CR */
nlflg = TRUE; /* New Line */
}
else buf[bufctr++] = c; /* Store Char */
}
/* Send Block */
tries = 0; /* Set Try Count */
if (bufctr) do {
putblock(env,buf,blocknum); /* Send Block */
rcode = recvbyte(10,bitmask); /* Get Response */
if (env->ftp == 1 && rcode == ESC)
rcode = recvbyte(10,bitmask);
if (rcode != ACK && env->logflg) {
fprintf(env->logfd, "%s Received on Block %d\n",
(rcode == TO) ? "Timeout" : "Non-ACK",
blocknum);
if (env->dbgflg) fprintf(env->dbgfd,
"%s Received on Block %d\n",
(rcode == TO) ? "Timeout" : "Non-ACK",
blocknum);
}
}
while (rcode != ACK && ++tries < ERRMAX);
blocknum = (blocknum + 1) & bitmask;
if (tries == ERRMAX) { /* Error Abort */
sending = FALSE;
if (env->logflg) fprintf(env->logfd, "Error Abort\n");
if (env->dbgflg) fprintf(env->dbgfd, "Error Abort\n");
}
}
/* Cleanup After Send */
fclose(fd); /* Close File */
tries = 0;
if (env->ftp == 1) while (++tries < ERRMAX) sendbyte(EOT);
else {
sendbyte(EOT);
while (recvbyte(15,bitmask) != ACK && ++tries < ERRMAX)
sendbyte(EOT);
if (tries == ERRMAX && env->logflg) {
fprintf(env->logfd, "Remote System Not Completing\n");
if (env->dbgflg) fprintf(env->dbgfd,
"Remote System Not Completing\n");
}
}
if (env->logflg) fprintf(env->logfd, "Send Complete\n");
if (env->dbgflg) fprintf(env->dbgfd, "Send Complete\n");
if (env->arpa) resetarpa(); /* Binary on ARPA Net? */
binary(FALSE,TRUE); /* Leave Binary Mode */
sleep(3);
printf("\n");
}
X/* Send Buffer to Receiver */
putblock(env,buf,blocknum)
struct environ *env;
char *buf;
int blocknum;
{
int i, j, checksum, bitmask;
int dbgcnt;
char dbgchr[20];
if (env->bit7) bitmask = 0x7f;
else bitmask = 0xff;
sendbyte(SOH); /* Send Start of Header */
if (env->dbgflg) fprintf(env->dbgfd, "SOH = %2x ", SOH);
if (env->ftp == 1) {
sendbyte(0); /* FTP 1 Data Packet */
if (env->dbgflg) fprintf(env->dbgfd, "00 ");
}
if (env->arpa && blocknum&bitmask == IAC) {
sendbyte(IAC);
if (env->dbgflg) fprintf(env->dbgfd, "%2x ", IAC);
}
sendbyte(blocknum&bitmask); /* Send Block Number */
if (env->dbgflg) fprintf(env->dbgfd, "%2x ", blocknum&bitmask);
if (env->arpa && (-blocknum-1)&bitmask == IAC) {
sendbyte(IAC);
if (env->dbgflg) fprintf(env->dbgfd, "%2x ", IAC);
}
sendbyte((-blocknum-1)&bitmask); /* Send Block Complement */
if (env->dbgflg) fprintf(env->dbgfd, "%2x ", (-blocknum-1)&bitmask);
if (env->ftp == 1) {
sendbyte(STX); /* FTP 1 Start of Text */
if (env->dbgflg) fprintf(env->dbgfd, "%2x ", STX);
}
checksum = 0;
dbgcnt = 0;
if (env->dbgflg) fprintf(env->dbgfd, "\n");
for (i = 0; i < BLOCKSZ; i++) {
if (env->arpa && *buf&bitmask == IAC) {
sendbyte(IAC);
if (env->dbgflg) {
fprintf(env->dbgfd, "%2x ", IAC);
dbgchr[dbgcnt++] = IAC;
}
}
sendbyte(*buf&bitmask); /* Send Byte */
if (env->dbgflg) {
dbgchr[dbgcnt++] = *buf&0x7f;
fprintf(env->dbgfd, "%2x ", *buf&0xff);
if (((i+1) % 8) == 0) {
fprintf(env->dbgfd, " |");
for (j=0; j<dbgcnt; j++) {
if (dbgchr[j] < ' ')
fprintf(env->dbgfd, ".");
else
fprintf(env->dbgfd, "%c", dbgchr[j]);
}
fprintf(env->dbgfd, "|\n");
dbgcnt = 0;
}
}
checksum = (checksum + *buf++) & bitmask;
}
if (env->dbgflg)
if (dbgcnt) {
fprintf(env->dbgfd, " |");
for (j=0; j<dbgcnt; j++) {
if (dbgchr[j] < ' ')
fprintf(env->dbgfd, ".");
else
fprintf(env->dbgfd, "%c", dbgchr[j]);
}
fprintf(env->dbgfd, "|\n");
}
if (env->ftp == 1) {
sendbyte(ETX); /* FTP 1 End of Text */
if (env->dbgflg) fprintf(env->dbgfd, "ETX = %2x ", ETX);
}
if (env->arpa && checksum&bitmask == IAC) {
sendbyte(IAC);
if (env->dbgflg) fprintf(env->dbgfd, "%2x ", IAC);
}
sendbyte(checksum&bitmask); /* Checksum */
if (env->dbgflg) fprintf(env->dbgfd, "Checksum = %2x ",
checksum&bitmask);
if (env->ftp == 1) {
sendbyte(ENQ); /* FTP 1 Enquiry */
if (env->dbgflg) fprintf(env->dbgfd, "ENQ = %2x ", ENQ);
}
if (env->dbgflg) fprintf(env->dbgfd, "\nEnd of Packet\n");
}
X/* Receive File */
recv(env,filename)
struct environ *env;
char *filename;
{
int fd; /* file descriptor */
int blocknum; /* next block to receive */
int rbcnt; /* total number of received blocks */
int errorcnt; /* number of errors on current block */
int receiving; /* continuation flag */
int char1; /* first char received in block */
int delay; /* inter-char delay for FTP */
int rcode; /* received block code */
int bitmask; /* 7/8 bit mask */
/* Set Mask for 7/8 Bits */
if (env->bit7) bitmask = 0x7f;
else bitmask = 0xff;
if (!access(filename,2)) {
printf("File %s Exists -- Delete it? ", filename);
if (!getyn()) {
printf("Aborting\n");
if (env->logflg) fprintf(env->logfd,
"Overwrite of %s Disallowed\n", filename);
if (env->dbgflg) fprintf(env->dbgfd,
"Overwrite of %s Disallowed\n", filename);
return;
}
}
unlink(filename); /* delete old file, if any */
if ((fd = creat(filename, CREATE)) == -1) { /* can't create */
printf("Can't Create %s\n", filename);
if (env->logflg) fprintf(env->logfd,
"Can't Create %s\n", filename);
if (env->dbgflg) fprintf(env->dbgfd,
"Can't Create %s\n", filename);
return;
}
/* We Have a GO */
printf("UC Receiving %s File: %s\n",
(env->filetype == 't') ? "Text" : "Binary",
filename);
if (env->logflg)
fprintf(env->logfd, "\nUC Receiving File: %s\n", filename);
if (env->dbgflg)
fprintf(env->dbgfd, "\nUC Receiving File: %s\n", filename);
printf("FTP %c, %c-Bit Transmission Selected\n",
(env->ftp == 1) ? '1' : '3',
env->bit7 ? '7' : '8');
if (env->logflg)
fprintf(env->logfd, "FTP %c, %c-Bit Transmission Selected\n",
(env->ftp == 1) ? '1' : '3',
env->bit7 ? '7' : '8');
if (env->dbgflg)
fprintf(env->dbgfd, "FTP %c, %c-Bit Transmission Selected\n",
(env->ftp == 1) ? '1' : '3',
env->bit7 ? '7' : '8');
printf("Ready to Receive\n");
/* Init Counters et al */
blocknum = 1;
rbcnt = 0;
errorcnt = 0;
receiving = TRUE;
/* Establish Binary Communications */
binary(TRUE,TRUE);
if (env->arpa) setarpa();
/* Synchronize with Sender */
if (env->ftp == 1) {
while (recvbyte(4,bitmask) != SYN);
sendbyte(ACK);
}
else sendbyte(NAK);
/* Receive Next Packet */
while (receiving) {
do {
char1 = recvbyte(6,bitmask);
}
while ((char1 != SOH) && (char1 != EOT) && (char1 != TO));
switch (char1) {
case TO : /* Timeout */
if (env->logflg)
fprintf(env->logfd, "Timeout on Block %d\n",
blocknum);
if (env->dbgflg)
fprintf(env->dbgfd, "Timeout on Block %d\n",
blocknum);
if (++errorcnt == ERRMAX) {
close(fd); /* Close File */
sleep(3); /* Delay for Sender */
if (env->arpa) resetarpa(); /* ARPA Net */
binary(FALSE,TRUE); /* Normal I/O */
if (env->logflg)
fprintf(env->logfd, "Error Abort\n");
if (env->dbgflg)
fprintf(env->dbgfd, "Error Abort\n");
receiving = FALSE;
}
sendbyte(NAK);
break;
case EOT : /* End of Transmission */
if (env->ftp == 3) sendbyte(ACK);
while (recvbyte(3,bitmask) != TO);
close(fd); /* Close File */
sleep(3); /* Delay for Sender */
if (env->arpa) resetarpa(); /* ARPA Net */
binary(FALSE,TRUE); /* Normal I/O */
if (env->logflg) {
fprintf(env->logfd, "Receive Complete -- ");
fprintf(env->logfd, "%dK, %d Blocks Received\n",
(rbcnt%8) ? (rbcnt/8)+1 : rbcnt/8,
rbcnt);
}
if (env->dbgflg) {
fprintf(env->dbgfd, "Receive Complete -- ");
fprintf(env->dbgfd, "%dK, %d Blocks Received\n",
(rbcnt%8) ? (rbcnt/8)+1 : rbcnt/8,
rbcnt);
}
printf("\n");
receiving = FALSE;
break;
case SOH : /* New or Old Block */
rcode = getblock(env,fd,blocknum); /* read block */
switch (rcode) {
case 0 : /* OK */
blocknum = ++blocknum & bitmask;
rbcnt++;
case 2 : /* OK, but Duplicate Block */
errorcnt = 0;
if (env->ftp == 1) sendbyte(ESC);
sendbyte(ACK);
if (env->dbgflg) fprintf(env->dbgfd,
"\nSending ACK\n");
break;
case 1 : /* Xmit Error, Non-Fatal */
if (++errorcnt < ERRMAX) {
sendbyte(NAK);
if (env->dbgflg) fprintf(env->dbgfd,
"\nSending NAK\n");
break;
}
default : /* Xmit Error, Fatal */
if (env->logflg)
fprintf(env->logfd, "Error Abort\n");
if (env->dbgfd)
fprintf(env->dbgfd, "Error Abort\n");
close(fd);
if (env->ftp == 1) sendbyte(ESC);
sendbyte(CAN);
if (env->arpa) resetarpa(); /* ARPA Net */
binary(FALSE,TRUE);
while (recvbyte(3,bitmask) != TO);
receiving = FALSE;
break;
}
break;
}
}
}
X/* Get Block from Sender */
getblock(env,fd,blocknum)
struct environ *env;
int fd, blocknum;
{
int curblock, cmpblock, delay, bitmask;
int recdone, checksum, inchecksum, byte, bufcnt, c;
int startstx, endetx, endenq;
int errflg, errchr;
char buff[BLOCKSZ];
int j, dbgcnt;
char dbgchr[20];
if (env->ftp == 1) {
delay = 5; /* FTP 1 Delay Constant */
recvbyte(5,bitmask); /* Flush Leading Zero */
}
else delay = 3; /* FTP 3 Delay Constant */
if (env->bit7) bitmask = 0x7f;
else bitmask = 0xff;
curblock = recvbyte(delay,bitmask);
if (env->dbgflg) fprintf(env->dbgfd, "Block Number = %4x\n", curblock);
if (curblock == TO) {
if (env->logflg)
fprintf(env->logfd, "Timeout on Block Number\n");
return(1);
}
cmpblock = recvbyte(delay,bitmask);
if (env->dbgflg) fprintf(env->dbgfd, "Block Compl = %4x\n", cmpblock);
if (curblock == TO) {
if (env->logflg)
fprintf(env->logfd, "Timeout on Block Complement\n");
return(1);
}
if (env->ftp == 1) {
startstx = recvbyte(delay,bitmask);
if (env->dbgflg) fprintf(env->dbgfd, "STX = %4x\n", startstx);
if (startstx == TO) {
if (env->logflg)
fprintf(env->logfd, "Timeout on STX\n");
return(1);
}
}
if ((curblock + cmpblock) != bitmask) {
if (env->logflg) fprintf(env->logfd,
"Block Number Error on Block %d\n", blocknum);
if (env->dbgflg) fprintf(env->dbgfd,
"Block Number Error on Block %d\n", blocknum);
while (recvbyte(delay,bitmask) != TO); /* Flush */
return(1);
}
checksum = 0; /* Init Checksum */
dbgcnt = 0; /* Init Char Count */
byte = 0; /* Init Buff Ptr */
recdone = FALSE; /* File Receive NOT Done */
for (bufcnt=0; bufcnt<BLOCKSZ; bufcnt++) {
c = recvbyte(delay,bitmask);
if (env->dbgflg) {
dbgchr[dbgcnt++] = c&0x7f;
fprintf(env->dbgfd, "%2x ", c&0xff);
if (((bufcnt+1) % 8) == 0) {
fprintf(env->dbgfd, " |");
for (j=0; j<dbgcnt; j++) {
if (dbgchr[j] < ' ')
fprintf(env->dbgfd, ".");
else
fprintf(env->dbgfd, "%c", dbgchr[j]);
}
dbgcnt = 0;
fprintf(env->dbgfd, "|\n");
}
}
if (c == TO) {
if (env->logflg)
fprintf(env->logfd, "Timeout on Block Recv\n");
if (env->dbgflg)
fprintf(env->dbgfd, "Timeout on Block Recv\n");
return(1);
}
buff[byte] = c;
checksum = (checksum + c) & bitmask;
if (env->filetype != 't') {
byte++; /* binary xfer, so advance */
continue;
}
if (c == CR) continue; /* skip CR */
if (c == CTRLZ) { /* done */
recdone = TRUE;
continue;
}
if (!recdone) byte++; /* continue */
}
if (env->ftp == 1) {
endetx = recvbyte(delay,bitmask);
if (env->dbgflg) fprintf(env->dbgfd, "ETX = %4x\n", endetx);
if (endetx == TO) {
if (env->logflg)
fprintf(env->logfd, "Timeout on ETX\n");
return(1);
}
}
inchecksum = recvbyte(delay,bitmask);
if (env->dbgflg)
fprintf(env->dbgfd,
"Computed Checksum = %4x Received Checksum = %4x\n",
checksum, inchecksum);
if (inchecksum == TO) {
if (env->logflg)
fprintf(env->logfd, "Timeout on Checksum\n");
return(1);
}
if (env->ftp == 1) {
endenq = recvbyte(delay,bitmask);
if (env->dbgflg) fprintf(env->dbgfd, "ENQ = %4x\n", endenq);
if (endenq == TO) {
if (env->logflg)
fprintf(env->logfd, "Timeout on ENQ\n");
return(1);
}
}
if (env->dbgflg) fprintf(env->dbgfd, "\nEnd of Packet\n");
errflg = FALSE;
if (env->ftp == 1) {
if (startstx != STX) {
errflg = TRUE;
errchr = STX;
}
if (endetx != ETX) {
errflg = TRUE;
errchr = ETX;
}
if (endenq != ENQ) {
errflg = TRUE;
errchr = ENQ;
}
if (errflg && env->logflg) {
fprintf(env->logfd, "Invalid Packet Control -- ");
switch (errchr) {
case STX :
fprintf(env->logfd,"STX");
break;
case ETX :
fprintf(env->logfd,"ETX");
break;
case ENQ :
fprintf(env->logfd,"ENQ");
break;
}
fprintf(env->logfd,"\n");
}
}
if (checksum != inchecksum) {
if (env->logflg)
fprintf(env->logfd,
"Checksum Error: Received %d/%xH vs Computed %d/%xH\n",
inchecksum, inchecksum, checksum, checksum);
errflg = TRUE;
}
if (errflg) return(1);
if (curblock != blocknum) {
if (curblock == (blocknum+1)&bitmask) {
if (env->logflg) fprintf(env->logfd,
"Phase Error\n");
if (env->dbgflg) fprintf(env->dbgfd,
"Phase Error\n");
return(99);
}
if (env->logflg)
fprintf(env->logfd, "Duplicate Block %d\n", blocknum);
if (env->dbgflg)
fprintf(env->dbgfd, "Duplicate Block %d\n", blocknum);
return(2);
}
if (write(fd,buff,byte) < 0) {
if (env->logflg) fprintf(env->logfd,
"File Write Error\n");
if (env->dbgflg) fprintf(env->dbgfd,
"File Write Error\n");
return(99);
}
return(0);
}
X/* Compute the CRC for a UNIX Text File */
crct(filename)
char *filename;
{
unsigned crcupd();
FILE *fd, *fopen();
unsigned crc;
int c;
int bytecnt;
/* open file for input */
if ((fd=fopen(filename, "r")) == NULL) {
printf("File %s Not Found\n", filename);
return;
}
/* init byte counter (for last block) */
bytecnt = 0;
/* init CRC Value */
crc = 0;
/* compute CRC on all bytes of file with file format conversion */
while ((c = getc(fd)) != EOF) {
bytecnt++;
if (!(bytecnt%BLOCKSZ)) bytecnt=0;
if (c == LF) {
crc = crcupd(CR, crc); /* Insert CR */
bytecnt++;
if (!(bytecnt%BLOCKSZ)) bytecnt=0;
}
crc = crcupd(c, crc); /* Update CRC */
}
/* fill last block with CTRLZ's */
for (;bytecnt < BLOCKSZ; bytecnt++) crc = crcupd(CTRLZ, crc);
/* close file */
fclose(fd);
/* print result */
printf("CRC of Text File %s is %4X\n", filename, crc);
}
X/* Compute a CRC Value for a Binary File */
crcb(filename)
char *filename;
{
unsigned crcupd();
FILE *fd, *fopen();
unsigned crc;
int c;
/* open file for input */
if ((fd=fopen(filename, "r")) == NULL) {
printf("File %s Not Found\n", filename);
return;
}
/* init CRC Value */
crc = 0;
/* compute CRC on all bytes of file */
while ((c = getc(fd)) != EOF) crc = crcupd(c, crc);
/* close file */
fclose(fd);
/* print result */
printf("CRC of Binary File %s is %4X\n", filename, crc);
}
X/* compute CRC on a byte-for-byte basis */
unsigned crcupd(byteval, crc)
int byteval;
unsigned crc;
{
unsigned newcrc, high, low;
newcrc = crc << 1; /* shift CRC left one bit */
high = newcrc & 0xff00; /* get new high byte */
low = (newcrc + byteval) & 0xff; /* get new low byte */
newcrc = high | low; /* OR the two bytes together */
if (crc & 0x8000) crc = newcrc ^ 0xa097; /* apply offset */
else crc = newcrc;
return(crc);
}
X/* File Status Display */
fstat(env,filename)
struct environ *env;
char *filename;
{
struct stat fsi; /* file status info */
if (stat (filename, &fsi) == -1) { /* get file status info */
printf("File %s Not Found\n", filename);
return;
}
printf("File Size of %s is %ldK, %ld Blocks\n",
filename,
fsi.st_size%1024 ? (fsi.st_size/1024)+1 : fsi.st_size/1024,
fsi.st_size%128 ? (fsi.st_size/128)+1 : fsi.st_size/128);
if (env->logflg) fprintf(env->logfd,
"File Size of %s is %ldK, %ld Blocks\n",
filename,
fsi.st_size%1024 ? (fsi.st_size/1024)+1 : fsi.st_size/1024,
fsi.st_size%128 ? (fsi.st_size/128)+1 : fsi.st_size/128);
}
X/* SUPPORT ROUTINES */
X/* get yes or no response from user */
getyn()
{
int c;
c = charx(); /* get char */
if (c == 'y' || c == 'Y') {
printf("Yes\n");
return(TRUE);
}
else {
printf("No\n");
return(FALSE);
}
}
X/* get single char input */
charx()
{
int binary();
int c;
binary(TRUE,FALSE);
c = getchar();
binary(FALSE,FALSE);
return (c);
}
X/* set ARPA Net for 8-bit transfers */
setarpa()
{
sendbyte(IAC); /* Is A Command */
sendbyte(WILL); /* Command to SERVER TELNET (Host) */
sendbyte(TRBIN); /* Command is: Transmit Binary */
sendbyte(IAC); /* Is A Command */
sendbyte(DO); /* Command to TAC */
sendbyte(TRBIN); /* Command is: Transmit Binary */
sleep(3); /* wait for TAC to configure */
return;
}
X/* reset the ARPA Net */
resetarpa()
{
sendbyte(IAC); /* Is A Command */
sendbyte(WONT); /* Negative Command to SERVER TELNET (Host) */
sendbyte(TRBIN); /* Command is: Don't Transmit Binary */
sendbyte(IAC); /* Is A Command */
sendbyte(DONT); /* Negative Command to TAC */
sendbyte(TRBIN); /* Command is: Don't Transmit Binary */
return;
}
X/* send byte to receiver */
sendbyte(data)
char data;
{
write (1, &data, 1); /* write the byte */
}
X/* receive a byte from sender */
recvbyte(seconds,bitmask)
unsigned seconds;
int bitmask;
{
char c;
int alarmfunc(); /* forward declaration */
signal(SIGALRM,alarmfunc); /* catch alarms */
alarm(seconds); /* set clock */
if (read (0, &c, 1) < 0) /* get char or timeout */
return (TO);
alarm(0); /* clear clock */
return (c&bitmask);
}
X/* dummy alarm function */
alarmfunc()
{
return;
}
X/* set and clear binary mode */
binary(setflg,scope)
int setflg, scope;
{
static struct sgttyb ttys, ttysold;
static struct stat statbuf;
if (setflg) { /* set binary */
if (gtty (0, &ttys) < 0) return(FALSE); /* failed */
ttysold.sg_ispeed = ttys.sg_ispeed; /* save old values */
ttysold.sg_ospeed = ttys.sg_ospeed;
ttysold.sg_erase = ttys.sg_erase;
ttysold.sg_kill = ttys.sg_kill;
ttysold.sg_flags = ttys.sg_flags;
ttys.sg_flags |= RAW; /* set for RAW Mode */
ttys.sg_flags &= ~ECHO; /* set no ECHO */
if (scope) { /* cover all values? */
ttys.sg_flags &= ~XTABS; /* set no tab exp */
ttys.sg_flags &= ~LCASE; /* set no case xlate */
ttys.sg_flags |= ANYP; /* set any parity */
ttys.sg_flags &= ~NL3; /* no delays on nl */
ttys.sg_flags &= ~TAB0; /* no tab delays */
ttys.sg_flags &= ~TAB1;
ttys.sg_flags &= ~CR3; /* no CR delay */
ttys.sg_flags &= ~FF1; /* no FF delay */
ttys.sg_flags &= ~BS1; /* no BS delay */
}
if (stty (0, &ttys) < 0) return(FALSE); /* failed */
if (scope) system("mesg n"); /* turn off messages */
return(TRUE);
}
else { /* clear binary */
if (stty (0, &ttysold) < 0) return (FALSE);
if (scope) system("mesg y"); /* turn on messages */
return(TRUE); /* OK */
}
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
/bin/chmod 644 uc.c
/bin/echo -n ' '; /bin/ls -ld uc.c
fi