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