home *** CD-ROM | disk | FTP | other *** search
-
-
- /**
- * Copyright (c) 1985 by Steve Wilhite, Worthington, Ohio
- *
- * Permission is granted to use or distribute this software without any
- * restrictions as long as this entire copyright notice is included intact.
- * You may include it in any software product that you sell for profit.
- *
- * This software is distributed as is, and is not guaranteed to work on any
- * given hardware/software configuration. Furthermore, no liability is
- * granted with this software.
- *
- * ABSTRACT:
- *
- * The function, Transfer_File, implements error-free file transfer using
- * CompuServe's "B" protocol.
- *
- * It has been assumed that the start-of-packet sequence, DLE "B", has
- * been detected and the next byte not received yet is the packet
- * sequence number (an ASCII digit).
- *
- * ENVIRONMENT: Lattice "C", machine independent.
- *
- * AUTHOR: Steve Wilhite, CREATION DATE: 21-Jul-85
- *
- * REVISION HISTORY:
- * Jan 1-3 1988 - Charles B. Blish - Formatted source properly. IMHO.
- * added 'short' specifier to vars,
- * removing 32 bit default of lattice.
- *
- *
- **/
-
- #include <lattice/stdio.h>
- #include <lattice/fcntl.h>
-
- /* External Functions */
-
- extern Delay(); /* Sleep for "n" milliseconds */
- extern Start_Timer(); /* Enable the timer for the specified number
- seconds */
- extern int Timer_Expired(); /* Returns "true" if the timer has expired,
- "false" otherwise */
- extern int Wants_To_Abort(); /* Returns "true" if the user wants to abort
- the file transfer, "false" otherwise */
- extern int Read_Modem(); /* Read a character from the comm port.
- Returns -1 if no character available */
- extern int Write_Modem(); /* Send a character to the comm port. Returns
- "true" is successful, "false" otherwise */
-
- extern int open(), creat(); /* standard I/O functions */
- extern int read(), write();
- extern int close();
-
- #define ETX 0x03
- #define ENQ 0x05
- #define DLE 0x10
- #define XON 0x11
- #define XOFF 0x13
- #define NAK 0x15
- #define NUL 0x00
-
- #define True 1
- #define False 0
- #define Success -1
- #define Failure 0
- #define Packet_Size 512
- #define Max_Errors 7
- #define Max_Time 15
- #define WACK ';' /* wait acknowledge */
-
- /* Sender actions */
-
- #define S_Send_Packet 0
- #define S_Get_DLE 1
- #define S_Get_Num 2
- #define S_Get_Seq 3
- #define S_Get_Data 4
- #define S_Get_Checksum 5
- #define S_Timed_Out 6
- #define S_Send_NAK 7
-
- /* Receiver actions */
-
- #define R_Get_DLE 0
- #define R_Get_B 1
- #define R_Get_Seq 2
- #define R_Get_Data 3
- #define R_Get_Checksum 4
- #define R_Send_NAK 5
- #define R_Send_ACK 6
-
-
- /* variables used by external routine that shows transfer status
- * in window - provided by user: CBB, Jan/88
- */
- extern int datablocks,dataretrys,oursize;
- /******************************************/
-
- /* routines called for handling status window;
- * provided by user: CBB Jan/88
- */
-
- extern int updl(); /* update download status window numbers */
- extern int svcdl(); /* check for wants to abort by windowclose */
- /* abort is forced by pushing ^C into the */
- /* users kbd input */
- extern int upul(); /* update upload status report window #'s */
- extern int svcup(); /* check for wants to abort by windowclose */
- /* abort is forced by pushing ^C into the */
- /* users kbd input */
- extern int showdownload(); /* opens window for status, calls recieve */
- extern int showupload(); /* opens window for status, calls transmit */
-
- /******************************************/
-
- static short Checksum;
-
- static short
- Ch,
- Seq_Num,
- R_Size, /* Size of receiver buffer */
- XOFF_Flag,
- Seen_ETX;
-
- static unsigned char
- S_Buffer[Packet_Size], /* Sender buffer */
- R_Buffer[Packet_Size]; /* Receiver buffer */
-
- static Put_Msg(Text)
-
- char *Text;
- {
- while (*Text != 0) Put_Char(*Text++);
- Put_Char('\015');
- Put_Char('\012');
- return(0);
- }
-
-
-
- static Send_Byte(Ch)
- int Ch;
- {
- short TCh;
- /* Listen for XOFF from the network */
- do
- {
- while ((TCh = Read_Modem()) >= 0)
- {
- if (TCh == XON)
- {
- XOFF_Flag = False;
- }
- else if (TCh == XOFF)
- {
- XOFF_Flag = True;
- }
- }
- } while (XOFF_Flag);
- while (!Write_Modem(Ch));
- return(0);
- }
-
-
-
- static Send_Masked_Byte(Ch)
- short Ch;
- {
- /* Mask any protocol or flow characters */
- /*PutChar(Ch);*/
- if (Ch == ETX || Ch == NUL || Ch == ENQ || Ch == DLE || Ch == NAK || Ch == XON || Ch == XOFF)
- {
- Send_Byte(DLE);
- Send_Byte(Ch + '@');
- }
- else
- {
- Send_Byte(Ch);
- }
- return(0);
- }
-
-
-
- static Send_ACK()
- {
- Send_Byte(DLE);
- Send_Byte(Seq_Num + '0');
- return(0);
- }
-
-
-
- static Read_Byte()
- {
- if ((Ch = Read_Modem()) < 0)
- {
- Start_Timer(Max_Time);
- do
- {
- if (Timer_Expired()) return Failure;
- } while ((Ch = Read_Modem()) < 0);
- }
- return Success;
- }
-
-
-
- static Read_Masked_Byte()
- {
- Seen_ETX = False;
- if (Read_Byte() == Failure) return Failure;
- /*PutChar(Ch);*/
- if (Ch == DLE)
- {
- if (Read_Byte() == Failure) return Failure;
- Ch &= 0x1F;
- }
- else if (Ch == ETX) Seen_ETX = True;
- return Success;
- }
-
-
-
-
- static Do_Checksum(Ch)
- short Ch;
- {
- Checksum <<= 1;
- if (Checksum > 255)
- {
- Checksum = (Checksum & 0xFF) + 1;
- }
- Checksum += Ch;
- if (Checksum > 255)
- {
- Checksum = (Checksum & 0xFF) + 1;
- }
- return(0);
- }
-
-
-
- static int Read_Packet(Action)
- /**
- * Function:
- * Receive a packet from the host.
- *
- * Inputs:
- * Action -- the starting action
- *
- * Outputs:
- * R_Buffer -- contains the packet just received
- * R_Size -- length of the packet
- *
- * Returns:
- * success/failure
- **/
- int Action;
- {
- short Errors;
- short Next_Seq;
-
- Errors = 0;
- updl(); /* update download window */
- svcdl(); /* watch for user close... */
- while (Errors < Max_Errors)
- {
- switch (Action)
- {
- case R_Get_DLE:
- {
- if (Read_Byte() == Failure)
- {
- Action = R_Send_NAK;
- }
- else if (Ch == DLE)
- {
- Action = R_Get_B;
- }
- else if (Ch == ENQ)
- {
- Action = R_Send_ACK;
- }
- break;
- }
- case R_Get_B:
- {
- if (Read_Byte() == Failure)
- {
- Action = R_Send_NAK;
- }
- else if (Ch == 'B')
- {
- Action = R_Get_Seq;
- }
- else
- {
- Action = R_Get_DLE;
- }
- break;
- }
- case R_Get_Seq:
- {
- if (Read_Byte() == Failure)
- {
- Action = R_Send_NAK;
- }
- else
- {
- Checksum = 0;
- Next_Seq = Ch - '0';
- Do_Checksum(Ch);
- R_Size = 0;
- Action = R_Get_Data;
- }
- break;
- }
- case R_Get_Data:
- {
- if (Read_Masked_Byte() == Failure)
- {
- Action = R_Send_NAK;
- }
- else if (Seen_ETX)
- {
- Action = R_Get_Checksum;
- }
- else if (R_Size == Packet_Size)
- {
- Action = R_Send_NAK;
- }
- else
- {
- R_Buffer[R_Size++] = Ch;
- Do_Checksum(Ch);
- }
- break;
- }
- case R_Get_Checksum:
- {
- Do_Checksum(ETX);
- if (Read_Masked_Byte() == Failure)
- {
- Action = R_Send_NAK;
- }
- else if (Checksum != Ch)
- {
- Action = R_Send_NAK;
- }
- else if (Next_Seq == Seq_Num)
- {
- Action = R_Send_ACK; /* Ignore duplicate packet */
- }
- else if (Next_Seq != (Seq_Num + 1) % 10)
- {
- Action = R_Send_NAK;
- }
- else
- {
- Seq_Num = Next_Seq;
- datablocks++;
- return Success;
- }
- break;
- }
- case R_Send_NAK:
- {
- Errors++;
- dataretrys++;
- Send_Byte(NAK);
- Action = R_Get_DLE;
- break;
- case R_Send_ACK:
- Send_ACK();
- Action = R_Get_DLE;
- break;
- }
- }
- }
-
- return Failure;
- }
-
-
-
- static int Send_Packet(Size)
- /**
- * Function:
- * Send the specified packet to the host.
- *
- * Inputs:
- * Size -- length of the packet
- * S_Buffer -- the packet to send
- *
- * Outputs:
- *
- * Returns:
- * success/failure
- **/
- short Size; /* size of packet to send */
- {
- short Action;
- short Next_Seq;
- short RCV_Num;
- short I;
- short Errors;
- Next_Seq = (Seq_Num + 1) % 10;
- Errors = 0;
- Action = S_Send_Packet;
- upul(); /* update download window */
- svcdl(); /* watch for user close... */
- while (Errors < Max_Errors)
- {
- switch (Action)
- {
- case S_Send_Packet:
- {
- Checksum = 0;
- Send_Byte(DLE);
- Send_Byte('B');
- Send_Byte(Next_Seq + '0');
- Do_Checksum(Next_Seq + '0');
- for (I = 0; I < Size; I++)
- {
- Send_Masked_Byte(S_Buffer[I]);
- Do_Checksum(S_Buffer[I]);
- }
- Send_Byte(ETX);
- Do_Checksum(ETX);
- Send_Masked_Byte(Checksum);
- Action = S_Get_DLE;
- break;
- }
- case S_Get_DLE:
- {
- if (Read_Byte() == Failure)
- {
- Action = S_Timed_Out;
- }
- else if (Ch == DLE)
- {
- Action = S_Get_Num;
- }
- else if (Ch == NAK)
- {
- Errors++;
- dataretrys++;
- Action = S_Send_Packet;
- }
- break;
- }
- case S_Get_Num:
- {
- if (Read_Byte() == Failure)
- {
- Action = S_Timed_Out;
- }
- else if (Ch >= '0' && Ch <= '9')
- {
- if (Ch == Seq_Num + '0')
- {
- Action = S_Get_DLE; /* Ignore duplicate ACK */
- }
- else if (Ch == Next_Seq + '0')
- {
- /* Correct sequence number */
- Seq_Num = Next_Seq;
- return Success;
- }
- else
- {
- if (Errors == 0)
- {
- Action = S_Send_Packet;
- }
- else
- {
- Action = S_Get_DLE;
- }
- }
- }
- else if (Ch == WACK)
- {
- /*Delay(50); Sleep for 5 seconds */
- Action = S_Get_DLE;
- }
- else if (Ch == 'B')
- {
- Action = S_Get_Seq;
- }
- else
- {
- Action = S_Get_DLE;
- }
- break;
- }
- case S_Get_Seq:
- {
- /**
- * Start of a "B" protocol packet. The only packet that makes
- * any sense here is a failure packet.
- **/
- if (Read_Byte() == Failure)
- {
- Action = S_Send_NAK;
- }
- else
- {
- Checksum = 0;
- RCV_Num = Ch - '0';
- Do_Checksum(Ch);
- I = 0;
- Action = S_Get_Data;
- }
- break;
- }
- case S_Get_Data:
- {
- if (Read_Masked_Byte() == Failure)
- {
- Action = S_Send_NAK;
- }
- else if (Seen_ETX)
- {
- Action = S_Get_Checksum;
- }
- else if (I == Packet_Size)
- {
- Action = S_Send_NAK;
- }
- else
- {
- R_Buffer[I++] = Ch;
- Do_Checksum(Ch);
- }
- break;
- }
- case S_Get_Checksum:
- {
- Do_Checksum(ETX);
- if (Read_Masked_Byte() == Failure)
- {
- Action = S_Send_NAK;
- }
- else if (Checksum != Ch)
- {
- Action = S_Send_NAK;
- }
- else if (RCV_Num != (Next_Seq + 1) % 10)
- {
- Action = S_Send_NAK;
- }
- else
- {
- /**
- * Assume the packet is failure packet. It makes no
- * difference since any other type of packet would be
- * invalid anyway. Return failure to caller.
- **/
- Errors = Max_Errors;
- }
- break;
- }
- case S_Timed_Out:
- {
- Errors++;
- dataretrys++;
- Action = S_Get_DLE;
- break;
- }
- case S_Send_NAK:
- {
- Errors++;
- dataretrys++;
- Send_Byte(NAK);
- Action = S_Get_DLE;
- break;
- }
- }
- }
- toterm("");
- toterm("Too many Errors!");
- toterm("");
- return Failure;
- }
-
-
-
- static Send_Failure(Code)
- /**
- * Function:
- * Send a failure packet to the host.
- *
- * Inputs:
- * Code -- failure code
- *
- * Outputs:
- *
- * Returns:
- **/
- char Code;
- {
- S_Buffer[0] = 'F';
- S_Buffer[1] = Code;
- Send_Packet(2);
- return(0);
- }
-
-
-
- int Receive_File(Name)
- /**
- * Function:
- * Download the specified file from the host.
- *
- * Inputs:
- * Name -- ptr to the file name string
- *
- * Outputs:
- *
- * Returns:
- * success/failure
- **/
- char *Name;
- {
- int Data_File;
- if ((Data_File = creat(Name, O_RAW)) == -1)
- {
- Put_Msg("Cannot create file");
- Send_Failure('E');
- return Failure;
- }
- Send_ACK();
- for (;;)
- {
- if (Read_Packet(R_Get_DLE) == Success)
- {
- switch (R_Buffer[0])
- {
- case 'N': /* Data packet */
- {
- Send_ACK();
- if (write(Data_File, &R_Buffer[1], R_Size - 1) != R_Size - 1)
- {
- /* Disk write error */
- Put_Msg("Disk write error");
- Send_Failure('E');
- close(Data_File);
- return Failure;
- }
- if (Wants_To_Abort())
- {
- /* The user wants to kill the transfer */
- Send_Failure('A');
- close(Data_File);
- return Failure;
- }
- break;
- }
- case 'T': /* Transfer packet */
- {
- if (R_Buffer[1] == 'C') /* Close file */
- {
- Send_ACK();
- close(Data_File);
- return Success;
- }
- else
- {
- /**
- * Unexpected "T" packet. Something is rotten on the
- * other end. Send a failure packet to kill the
- * transfer cleanly.
- **/
- Put_Msg("Unexpected packet type");
- Send_Failure('E');
- close(Data_File);
- return Failure;
- }
- }
- case 'F': /* Failure packet */
- {
- Send_ACK();
- close(Data_File);
- return Failure;
- }
- default:
- {
- return Failure;
- }
- }
- }
- else
- {
- close(Data_File);
- return Failure;
- }
- }
- return Failure;
- }
-
-
-
- int Send_File(Name)
- /**
- * Function:
- * Send the specified file to the host.
- *
- * Inputs:
- * Name -- ptr to the file name string
- *
- * Outputs:
- *
- * Returns:
- * success/failure
- **/
- char *Name;
- {
- int Data_File;
- short N;
- if ((Data_File = open(Name, O_RDONLY | O_RAW)) == -1)
- {
- Put_Msg("Cannot access that file");
- Send_Failure('E');
- return Failure;
- }
- do
- {
- S_Buffer[0] = 'N';
- N = read(Data_File, &S_Buffer[1], Packet_Size - 1);
- if (N > 0)
- {
- if (Send_Packet(N + 1) == Failure)
- {
- close(Data_File);
- return Failure;
- }
- if (Wants_To_Abort())
- {
- Send_Failure('A');
- close(Data_File);
- return Failure;
- }
- datablocks++;
- }
- } while (N > 0);
- if (N == 0) /* end of file */
- {
- close(Data_File);
- S_Buffer[0] = 'T';
- S_Buffer[1] = 'C';
- return Send_Packet(2);
- }
- else
- {
- Put_Msg("Disk read error");
- Send_Failure('E');
- return Failure;
- }
- return Failure;
- }
-
-
-
-
-
- /*
- B PROTOCOL SUMMARY
-
-
-
- Block Format:
-
- <DLE><B><blknum+48><command><contents><ETX><checksum>
-
-
- Defined commands are:
-
- Remote Memory Reference
-
- B Load memory
- G Go execute at an address
-
- File Transfer Mode
-
- TE<file name> Erase a file
- TR<present><0><new> Rename a file
- TU<type><file name> Transfer from Remote to Host
- TD<type><file name> Transfer from Host to Remote
- N Next block of data
- TC Close file
-
- <type> is:
- A - ASCII
- B - Binary
- I - Image
-
- Failure Blocks
-
- FA Failure - Abort
- FC Failure - capacity problem
- FE Failure - error during processing of last block
- FN Failure - function not implemented
- */
-
- int Transfer_File()
- /**
- * Function:
- * Transfer a file from/to the macro to/from the host.
- *
- * Inputs:
- *
- * Outputs:
- *
- * Returns:
- * success/failure
- **/
- {
- short I, N,temp;
- char Name[64];
-
- XOFF_Flag = False;
- Seq_Num = 0;
- nowindow();
- if (Read_Packet(R_Get_Seq) == Success)
- {
- if (R_Buffer[0] == 'T') /* transfer packet */
- {
- if (R_Buffer[1] != 'D' && R_Buffer[1] != 'U') /*Check drctchun*/
- {
- Send_Failure('N'); /* not UPLOAD or DOWNLOAD */
- return Failure;
- }
- if (R_Buffer[2] != 'A' && R_Buffer[2] != 'B')/*Check file type*/
- {
- Send_Failure('N'); /* not ASCII or BINARY */
- return Failure;
- }
- if (R_Size - 3 > 63) /* Get the file name length */
- {
- N = 63;
- }
- else
- {
- N = R_Size - 3;
- }
- for (I = 0; I < N; I++) /* Now actually read the file name */
- {
- Name[I] = R_Buffer[I + 3];
- }
- Name[I] = 0; /* NULL terminate the string */
- if (R_Buffer[1] == 'U') /* Do Upload transfer */
- {
- oursize = findsize(Name);
- temp = showupload(Name);
- }
- else /* Do Download transfer */
- {
- temp = showdownload(Name);
- }
- nowindow();
- return((int)temp);
- }
- else
- {
- Send_Failure('E'); /* wrong type of packet */
- return Failure;
- }
- }
- else /* didn't read packet successfully */
- {
- return Failure;
- }
- return Failure;
- }
-
-
-