_KERMIT FOR OS/2_ by Brian R. Anderson [LISTING ONE] MODULE PCKermit; (**************************************************************************) (* *) (* PCKermit -- by Brian R. Anderson *) (* Copyright (c) 1990 *) (* *) (* PCKermit is an implementation of the Kermit file transfer protocol *) (* developed at Columbia University. This (OS/2 PM) version is a *) (* port from the DOS version of Kermit that I wrote two years ago. *) (* My original DOS version appeared in the May 1989 issue of DDJ. *) (* *) (* The current version includes emulation of the TVI950 Video Display *) (* Terminal for interaction with IBM mainframes (through the IBM 7171). *) (* *) (**************************************************************************) FROM SYSTEM IMPORT ADR; FROM OS2DEF IMPORT HAB, HWND, HPS, NULL, ULONG; FROM PMWIN IMPORT MPFROM2SHORT, HMQ, QMSG, CS_SIZEREDRAW, WS_VISIBLE, FS_ICON, FCF_TITLEBAR, FCF_SYSMENU, FCF_SIZEBORDER, FCF_MINMAX, FCF_ACCELTABLE, FCF_SHELLPOSITION, FCF_TASKLIST, FCF_MENU, FCF_ICON, SWP_MOVE, SWP_SIZE, SWP_MAXIMIZE, HWND_DESKTOP, FID_SYSMENU, SC_CLOSE, MIA_DISABLED, MM_SETITEMATTR, WinInitialize, WinCreateMsgQueue, WinGetMsg, WinDispatchMsg, WinSendMsg, WinRegisterClass, WinCreateStdWindow, WinDestroyWindow, WinWindowFromID, WinDestroyMsgQueue, WinTerminate, WinSetWindowText, WinSetWindowPos, WinQueryWindowPos; FROM KH IMPORT IDM_KERMIT; FROM Shell IMPORT Class, Title, Child, WindowProc, ChildWindowProc, FrameWindow, ClientWindow, SetPort, Pos; CONST QUEUE_SIZE = 1024; (* Large message queue for async events *) VAR AnchorBlock : HAB; MessageQueue : HMQ; Message : QMSG; FrameFlags : ULONG; hsys : HWND; BEGIN (* main *) AnchorBlock := WinInitialize(0); IF AnchorBlock # 0 THEN MessageQueue := WinCreateMsgQueue (AnchorBlock, QUEUE_SIZE); IF MessageQueue # 0 THEN (* Register the parent window class *) WinRegisterClass ( AnchorBlock, ADR (Class), WindowProc, CS_SIZEREDRAW, 0); (* Register a child window class *) WinRegisterClass ( AnchorBlock, ADR (Child), ChildWindowProc, CS_SIZEREDRAW, 0); (* Create a standard window *) FrameFlags := FCF_TITLEBAR + FCF_MENU + FCF_MINMAX + FCF_SYSMENU + FCF_SIZEBORDER + FCF_TASKLIST + FCF_ICON + FCF_SHELLPOSITION + FCF_ACCELTABLE; FrameWindow := WinCreateStdWindow ( HWND_DESKTOP, (* handle of the parent window *) WS_VISIBLE + FS_ICON, (* the window style *) FrameFlags, (* the window flags *) ADR(Class), (* the window class *) NULL, (* the title bar text *) WS_VISIBLE, (* client window style *) NULL, (* handle of resource module *) IDM_KERMIT, (* resource id *) ClientWindow (* returned client window handle *) ); IF FrameWindow # 0 THEN (* Disable the CLOSE item on the system menu *) hsys := WinWindowFromID (FrameWindow, FID_SYSMENU); WinSendMsg (hsys, MM_SETITEMATTR, MPFROM2SHORT (SC_CLOSE, 1), MPFROM2SHORT (MIA_DISABLED, MIA_DISABLED)); (* Expand Window to Nearly Full Size, And Display the Title *) WinQueryWindowPos (HWND_DESKTOP, ADR (Pos)); WinSetWindowPos (FrameWindow, 0, Pos.x + 3, Pos.y + 3, Pos.cx - 6, Pos.cy - 6, SWP_MOVE + SWP_SIZE); WinSetWindowText (FrameWindow, ADR (Title)); SetPort; (* Try to initialize communications port *) WHILE WinGetMsg(AnchorBlock, Message, NULL, 0, 0) # 0 DO WinDispatchMsg(AnchorBlock, Message); END; WinDestroyWindow(FrameWindow); END; WinDestroyMsgQueue(MessageQueue); END; WinTerminate(AnchorBlock); END; END PCKermit. [LISTING TWO] DEFINITION MODULE Shell; FROM OS2DEF IMPORT USHORT, HWND; FROM PMWIN IMPORT MPARAM, MRESULT, SWP; EXPORT QUALIFIED Class, Child, Title, FrameWindow, ClientWindow, ChildFrameWindow, ChildClientWindow, Pos, SetPort, WindowProc, ChildWindowProc; CONST Class = "PCKermit"; Child ="Child"; Title = "PCKermit -- Microcomputer to Mainframe Communications"; VAR FrameWindow : HWND; ClientWindow : HWND; ChildFrameWindow : HWND; ChildClientWindow : HWND; Pos : SWP; (* Screen Dimensions: position & size *) comport : CARDINAL; PROCEDURE SetPort; PROCEDURE WindowProc ['WindowProc'] ( hwnd : HWND; msg : USHORT; mp1 : MPARAM; mp2 : MPARAM) : MRESULT [LONG, LOADDS]; PROCEDURE ChildWindowProc ['ChildWindowProc'] ( hwnd : HWND; msg : USHORT; mp1 : MPARAM; mp2 : MPARAM) : MRESULT [LONG, LOADDS]; END Shell. [LISTING THREE] DEFINITION MODULE Term; (* TVI950 Terminal Emulation For Kermit *) EXPORT QUALIFIED WM_TERM, WM_TERMQUIT, Dir, TermThrProc, InitTerm, PutKbdChar, PutPortChar; CONST WM_TERM = 4000H; WM_TERMQUIT = 4001H; PROCEDURE Dir (path : ARRAY OF CHAR); (* Displays a directory *) PROCEDURE TermThrProc; (* Thread to get characters from port, put into buffer, send message *) PROCEDURE InitTerm; (* Clear Screen, Home Cursor, Get Ready For Terminal Emulation *) PROCEDURE PutKbdChar (ch1, ch2 : CHAR); (* Process a character received from the keyboard *) PROCEDURE PutPortChar (ch : CHAR); (* Process a character received from the port *) END Term. [LISTING FOUR] DEFINITION MODULE Screen; (* Module to perform "low level" screen functions (via AVIO) *) FROM PMAVIO IMPORT HVPS; EXPORT QUALIFIED NORMAL, HIGHLIGHT, REVERSE, attribute, ColorSet, hvps, White, Green, Amber, Color1, Color2, ClrScr, ClrEol, GotoXY, GetXY, Right, Left, Up, Down, Write, WriteLn, WriteString, WriteInt, WriteHex, WriteAtt; VAR NORMAL : CARDINAL; HIGHLIGHT : CARDINAL; REVERSE : CARDINAL; attribute : CARDINAL; ColorSet : CARDINAL; hvps : HVPS; (* presentation space used by screen module *) PROCEDURE White; (* Sets up colors: Monochrome White *) PROCEDURE Green; (* Sets up colors: Monochrome Green *) PROCEDURE Amber; (* Sets up colors: Monochrome Amber *) PROCEDURE Color1; (* Sets up colors: Blue, Red, Green *) PROCEDURE Color2; (* Sets up colors: Green, Magenta, Cyan *) PROCEDURE ClrScr; (* Clear the screen, and home the cursor *) PROCEDURE ClrEol; (* clear from the current cursor position to the end of the line *) PROCEDURE Right; (* move cursor to the right *) PROCEDURE Left; (* move cursor to the left *) PROCEDURE Up; (* move cursor up *) PROCEDURE Down; (* move cursor down *) PROCEDURE GotoXY (col, row : CARDINAL); (* position cursor at column, row *) PROCEDURE GetXY (VAR col, row : CARDINAL); (* determine current cursor position *) PROCEDURE Write (c : CHAR); (* Write a Character, Teletype Mode *) PROCEDURE WriteString (str : ARRAY OF CHAR); (* Write String, Teletype Mode *) PROCEDURE WriteInt (n : INTEGER; s : CARDINAL); (* Write Integer, Teletype Mode *) PROCEDURE WriteHex (n, s : CARDINAL); (* Write a Hexadecimal Number, Teletype Mode *) PROCEDURE WriteLn; (* Write , Teletype Mode *) PROCEDURE WriteAtt (c : CHAR); (* write character and attribute at cursor position *) END Screen. [LISTING FIVE] DEFINITION MODULE PAD; (* Packet Assembler/Disassembler for Kermit *) FROM PMWIN IMPORT MPARAM; EXPORT QUALIFIED WM_PAD, PAD_Quit, PAD_Error, PacketType, yourNPAD, yourPADC, yourEOL, Aborted, sFname, Send, Receive, DoPADMsg; CONST WM_PAD = 5000H; PAD_Quit = 0; PAD_Error = 20; TYPE (* PacketType used in both PAD and DataLink modules *) PacketType = ARRAY [1..100] OF CHAR; VAR (* yourNPAD, yourPADC, and yourEOL used in both PAD and DataLink *) yourNPAD : CARDINAL; (* number of padding characters *) yourPADC : CHAR; (* padding characters *) yourEOL : CHAR; (* End Of Line -- terminator *) sFname : ARRAY [0..20] OF CHAR; Aborted : BOOLEAN; PROCEDURE Send; (* Sends a file after prompting for filename *) PROCEDURE Receive; (* Receives a file (or files) *) PROCEDURE DoPADMsg (mp1, mp2 : MPARAM); (* Output messages for Packet Assembler/Disassembler *) END PAD. [LISTING SIX] DEFINITION MODULE DataLink; (* Sends and Receives Packets for PCKermit *) FROM PMWIN IMPORT MPARAM; FROM PAD IMPORT PacketType; EXPORT QUALIFIED WM_DL, FlushUART, SendPacket, ReceivePacket, DoDLMsg; CONST WM_DL = 6000H; PROCEDURE FlushUART; (* ensure no characters left in UART holding registers *) PROCEDURE SendPacket (s : PacketType); (* Adds SOH and CheckSum to packet *) PROCEDURE ReceivePacket (VAR r : PacketType) : BOOLEAN; (* strips SOH and checksum -- returns status: TRUE= good packet *) (* received; FALSE = timed out waiting for packet or checksum error *) PROCEDURE DoDLMsg (mp1, mp2 : MPARAM); (* Process DataLink Messages *) END DataLink. [LISTING SEVEN] (*************************************************************) (* *) (* Copyright (C) 1988, 1989 *) (* by Stony Brook Software *) (* *) (* All rights reserved. *) (* *) (*************************************************************) DEFINITION MODULE CommPort; TYPE CommStatus = ( Success, InvalidPort, InvalidParameter, AlreadyReceiving, NotReceiving, NoCharacter, FramingError, OverrunError, ParityError, BufferOverflow, TimeOut ); BaudRate = ( Baud110, Baud150, Baud300, Baud600, Baud1200, Baud2400, Baud4800, Baud9600, Baud19200 ); DataBits = [7..8]; StopBits = [1..2]; Parity = (Even, Odd, None); PROCEDURE InitPort(port : CARDINAL; speed : BaudRate; data : DataBits; stop : StopBits; check : Parity) : CommStatus; PROCEDURE StartReceiving(port, bufsize : CARDINAL) : CommStatus; PROCEDURE StopReceiving(port : CARDINAL) : CommStatus; PROCEDURE GetChar(port : CARDINAL; VAR ch : CHAR) : CommStatus; PROCEDURE SendChar(port : CARDINAL; ch : CHAR; modem : BOOLEAN) : CommStatus; END CommPort. [LISTING EIGHT] DEFINITION MODULE Files; (* File I/O for Kermit *) FROM FileSystem IMPORT File; EXPORT QUALIFIED Status, FileType, Open, Create, CloseFile, Get, Put, DoWrite; TYPE Status = (Done, Error, EOF); FileType = (Input, Output); PROCEDURE Open (VAR f : File; name : ARRAY OF CHAR) : Status; (* opens an existing file for reading, returns status *) PROCEDURE Create (VAR f : File; name : ARRAY OF CHAR) : Status; (* creates a new file for writing, returns status *) PROCEDURE CloseFile (VAR f : File; Which : FileType) : Status; (* closes a file after reading or writing *) PROCEDURE Get (VAR f : File; VAR ch : CHAR) : Status; (* Reads one character from the file, returns status *) PROCEDURE Put (ch : CHAR); (* Writes one character to the file buffer *) PROCEDURE DoWrite (VAR f : File) : Status; (* Writes buffer to disk only if nearly full *) END Files. [LISTING NINE] IMPLEMENTATION MODULE Shell; FROM SYSTEM IMPORT ADDRESS, ADR; IMPORT ASCII; FROM OS2DEF IMPORT LOWORD, HIWORD, HWND, HDC, HPS, RECTL, USHORT, NULL, ULONG; FROM Term IMPORT WM_TERM, WM_TERMQUIT, Dir, TermThrProc, InitTerm, PutKbdChar, PutPortChar; FROM PAD IMPORT WM_PAD, PAD_Quit, PAD_Error, DoPADMsg, Aborted, sFname, Send, Receive; FROM DataLink IMPORT WM_DL, DoDLMsg; FROM Screen IMPORT hvps, ColorSet, White, Green, Amber, Color1, Color2, ClrScr, WriteLn; FROM DosCalls IMPORT DosCreateThread, DosSuspendThread, DosResumeThread, DosSleep; FROM PMAVIO IMPORT VioCreatePS, VioAssociate, VioDestroyPS, VioShowPS, WinDefAVioWindowProc, FORMAT_CGA, HVPS; FROM PMWIN IMPORT MPARAM, MRESULT, SWP, PSWP, WS_VISIBLE, FCF_TITLEBAR, FCF_SIZEBORDER, FCF_SHELLPOSITION, WM_SYSCOMMAND, WM_MINMAXFRAME, SWP_MINIMIZE, HWND_DESKTOP, WM_PAINT, WM_QUIT, WM_COMMAND, WM_INITDLG, WM_CONTROL, WM_HELP, WM_INITMENU, WM_SIZE, WM_DESTROY, WM_CREATE, WM_CHAR, BM_SETCHECK, MBID_OK, MB_OK, MB_OKCANCEL, KC_CHAR, KC_CTRL, KC_VIRTUALKEY, KC_KEYUP, SWP_SIZE, SWP_MOVE, SWP_MAXIMIZE, SWP_RESTORE, MB_ICONQUESTION, MB_ICONASTERISK, MB_ICONEXCLAMATION, FID_MENU, MM_SETITEMATTR, MM_QUERYITEMATTR, MIA_DISABLED, MIA_CHECKED, MPFROM2SHORT, WinCreateStdWindow, WinDestroyWindow, WinOpenWindowDC, WinSendMsg, WinQueryDlgItemText, WinInvalidateRect, WinDefWindowProc, WinBeginPaint, WinEndPaint, WinQueryWindowRect, WinSetWindowText, WinSetFocus, WinDlgBox, WinDefDlgProc, WinDismissDlg, WinMessageBox, WinPostMsg, WinWindowFromID, WinSendDlgItemMsg, WinSetWindowPos, WinSetActiveWindow; FROM PMGPI IMPORT GpiErase; FROM KH IMPORT IDM_KERMIT, IDM_FILE, IDM_OPTIONS, IDM_SENDFN, ID_SENDFN, IDM_DIR, IDM_CONNECT, IDM_SEND, IDM_REC, IDM_DIRPATH, ID_DIRPATH, IDM_DIREND, IDM_QUIT, IDM_ABOUT, IDM_HELPMENU, IDM_TERMHELP, IDM_COMPORT, IDM_BAUDRATE, IDM_DATABITS, IDM_STOPBITS, IDM_PARITY, COM_OFF, ID_COM1, ID_COM2, PARITY_OFF, ID_EVEN, ID_ODD, ID_NONE, DATA_OFF, ID_DATA7, ID_DATA8, STOP_OFF, ID_STOP1, ID_STOP2, BAUD_OFF, ID_B110, ID_B150, ID_B300, ID_B600, ID_B1200, ID_B2400, ID_B4800, ID_B9600, ID_B19K2, IDM_COLORS, IDM_WHITE, IDM_GREEN, IDM_AMBER, IDM_C1, IDM_C2; FROM CommPort IMPORT CommStatus, BaudRate, DataBits, StopBits, Parity, InitPort, StartReceiving, StopReceiving; FROM Strings IMPORT Assign, Append, AppendChar; CONST WM_SETMAX = 7000H; WM_SETFULL = 7001H; WM_SETRESTORE = 7002H; NONE = 0; (* no port yet initialized *) STKSIZE = 4096; BUFSIZE = 4096; (* Port receive buffers: room for two full screens *) PortError = "Port Is Already In Use -- EXIT? (Cancel Trys Another Port)"; ESC = 33C; VAR FrameFlags : ULONG; TermStack : ARRAY [1..STKSIZE] OF CHAR; Stack : ARRAY [1..STKSIZE] OF CHAR; TermThr : CARDINAL; Thr : CARDINAL; hdc : HDC; frame_hvps, child_hvps : HVPS; TermMode : BOOLEAN; Path : ARRAY [0..60] OF CHAR; Banner : ARRAY [0..40] OF CHAR; PrevComPort : CARDINAL; Settings : ARRAY [0..1] OF RECORD baudrate : CARDINAL; databits : CARDINAL; parity : CARDINAL; stopbits : CARDINAL; END; PROCEDURE SetFull; (* Changes window to full size *) BEGIN WinSetWindowPos (FrameWindow, 0, Pos.x + 3, Pos.y + 3, Pos.cx - 6, Pos.cy - 6, SWP_MOVE + SWP_SIZE); END SetFull; PROCEDURE SetRestore; (* Changes window to full size FROM maximized *) BEGIN WinSetWindowPos (FrameWindow, 0, Pos.x + 3, Pos.y + 3, Pos.cx - 6, Pos.cy - 6, SWP_MOVE + SWP_SIZE + SWP_RESTORE); END SetRestore; PROCEDURE SetMax; (* Changes window to maximized *) BEGIN WinSetWindowPos (FrameWindow, 0, Pos.x + 3, Pos.y + 3, Pos.cx - 6, Pos.cy - 6, SWP_MOVE + SWP_SIZE + SWP_MAXIMIZE); END SetMax; PROCEDURE SetBanner; (* Displays Abbreviated Program Title + Port Settings in Title Bar *) CONST PortName : ARRAY [0..1] OF ARRAY [0..5] OF CHAR = [["COM1:", 0C], ["COM2:", 0C]]; BaudName : ARRAY [0..8] OF ARRAY [0..5] OF CHAR = [["110", 0C], ["150", 0C], ["300", 0C], ["600", 0C], ["1200", 0C], ["2400", 0C], ["4800", 0C], ["9600", 0C], ["19200", 0C]]; ParityName : ARRAY [0..2] OF CHAR = ['E', 'O', 'N']; BEGIN WITH Settings[comport - COM_OFF] DO Assign (Class, Banner); Append (Banner, " -- "); Append (Banner, PortName[comport - COM_OFF]); Append (Banner, BaudName[baudrate - BAUD_OFF]); AppendChar (Banner, ','); AppendChar (Banner, ParityName[parity - PARITY_OFF]); AppendChar (Banner, ','); AppendChar (Banner, CHR ((databits - DATA_OFF) + 30H)); AppendChar (Banner, ','); AppendChar (Banner, CHR ((stopbits - STOP_OFF) + 30H)); WinSetWindowText (FrameWindow, ADR (Banner)); END; END SetBanner; PROCEDURE SetPort; (* Sets The Communications Parameters Chosen By User *) VAR status : CommStatus; rc : USHORT; BEGIN IF PrevComPort # NONE THEN StopReceiving (PrevComPort - COM_OFF); END; WITH Settings[comport - COM_OFF] DO status := InitPort ( comport - COM_OFF, BaudRate (baudrate - BAUD_OFF), DataBits (databits - DATA_OFF), StopBits (stopbits - STOP_OFF), Parity (parity - PARITY_OFF), ); END; IF status = Success THEN StartReceiving (comport - COM_OFF, BUFSIZE); PrevComPort := comport; ELSE rc := WinMessageBox (HWND_DESKTOP, FrameWindow, ADR (PortError), 0, 0, MB_OKCANCEL + MB_ICONEXCLAMATION); IF rc = MBID_OK THEN WinPostMsg (FrameWindow, WM_QUIT, 0, 0); ELSE (* try the other port *) IF comport = ID_COM1 THEN comport := ID_COM2; ELSE comport := ID_COM1; END; SetPort; (* recursive call for retry *) END; END; SetBanner; END SetPort; PROCEDURE MakeChild (msg : ARRAY OF CHAR); (* Creates a child window for use by send or receive threads *) VAR c_hdc : HDC; BEGIN WinPostMsg (FrameWindow, WM_SETFULL, 0, 0); Disable (IDM_CONNECT); Disable (IDM_SEND); Disable (IDM_REC); Disable (IDM_DIR); Disable (IDM_OPTIONS); Disable (IDM_COLORS); (* Create a client window *) FrameFlags := FCF_TITLEBAR + FCF_SIZEBORDER; ChildFrameWindow := WinCreateStdWindow ( ClientWindow, (* handle of the parent window *) WS_VISIBLE, (* the window style *) FrameFlags, (* the window flags *) ADR(Child), (* the window class *) NULL, (* the title bar text *) WS_VISIBLE, (* client window style *) NULL, (* handle of resource module *) IDM_KERMIT, (* resource id *) ChildClientWindow (* returned client window handle *) ); WinSetWindowPos (ChildFrameWindow, 0, Pos.cx DIV 4, Pos.cy DIV 4, Pos.cx DIV 2, Pos.cy DIV 2 - 3, SWP_MOVE + SWP_SIZE); WinSetWindowText (ChildFrameWindow, ADR (msg)); WinSetActiveWindow (HWND_DESKTOP, ChildFrameWindow); c_hdc := WinOpenWindowDC (ChildClientWindow); hvps := child_hvps; VioAssociate (c_hdc, hvps); ClrScr; (* clear the hvio window *) END MakeChild; PROCEDURE Disable (item : USHORT); (* Disables and "GREYS" a menu item *) VAR h : HWND; BEGIN h := WinWindowFromID (FrameWindow, FID_MENU); WinSendMsg (h, MM_SETITEMATTR, MPFROM2SHORT (item, 1), MPFROM2SHORT (MIA_DISABLED, MIA_DISABLED)); END Disable; PROCEDURE Enable (item : USHORT); (* Enables a menu item *) VAR h : HWND; atr : USHORT; BEGIN h := WinWindowFromID (FrameWindow, FID_MENU); atr := USHORT (WinSendMsg (h, MM_QUERYITEMATTR, MPFROM2SHORT (item, 1), MPFROM2SHORT (MIA_DISABLED, MIA_DISABLED))); atr := USHORT (BITSET (atr) * (BITSET (MIA_DISABLED) / BITSET (-1))); WinSendMsg (h, MM_SETITEMATTR, MPFROM2SHORT (item, 1), MPFROM2SHORT (MIA_DISABLED, atr)); END Enable; PROCEDURE Check (item : USHORT); (* Checks a menu item -- indicates that it is selected *) VAR h : HWND; BEGIN h := WinWindowFromID (FrameWindow, FID_MENU); WinSendMsg (h, MM_SETITEMATTR, MPFROM2SHORT (item, 1), MPFROM2SHORT (MIA_CHECKED, MIA_CHECKED)); END Check; PROCEDURE UnCheck (item : USHORT); (* Remove check from a menu item *) VAR h : HWND; atr : USHORT; BEGIN h := WinWindowFromID (FrameWindow, FID_MENU); atr := USHORT (WinSendMsg (h, MM_QUERYITEMATTR, MPFROM2SHORT (item, 1), MPFROM2SHORT (MIA_CHECKED, MIA_CHECKED))); atr := USHORT (BITSET (atr) * (BITSET (MIA_CHECKED) / BITSET (-1))); WinSendMsg (h, MM_SETITEMATTR, MPFROM2SHORT (item, 1), MPFROM2SHORT (MIA_CHECKED, atr)); END UnCheck; PROCEDURE DoMenu (hwnd : HWND; item : MPARAM); (* Processes Most Menu Interactions *) VAR rcl : RECTL; rc : USHORT; BEGIN CASE LOWORD (item) OF IDM_DIR: SetFull; WinQueryWindowRect (hwnd, rcl); WinDlgBox (HWND_DESKTOP, hwnd, PathDlgProc, 0, IDM_DIRPATH, 0); hvps := frame_hvps; VioAssociate (hdc, hvps); Dir (Path); WinDlgBox (HWND_DESKTOP, hwnd, DirEndDlgProc, 0, IDM_DIREND, 0); VioAssociate (0, hvps); WinInvalidateRect (hwnd, ADR (rcl), 0); | IDM_CONNECT: TermMode := TRUE; Disable (IDM_CONNECT); Disable (IDM_SEND); Disable (IDM_REC); Disable (IDM_DIR); Disable (IDM_OPTIONS); Disable (IDM_COLORS); (* MAXIMIZE Window -- Required for Terminal Emulation *) SetMax; hvps := frame_hvps; VioAssociate (hdc, hvps); DosResumeThread (TermThr); InitTerm; | IDM_SEND: WinDlgBox (HWND_DESKTOP, hwnd, SendFNDlgProc, 0, IDM_SENDFN, 0); MakeChild ("Send a File"); DosCreateThread (Send, Thr, ADR (Stack[STKSIZE])); | IDM_REC: MakeChild ("Receive a File"); DosCreateThread (Receive, Thr, ADR (Stack[STKSIZE])); | IDM_QUIT: rc := WinMessageBox (HWND_DESKTOP, ClientWindow, ADR ("Do You Really Want To EXIT PCKermit?"), ADR ("End Session"), 0, MB_OKCANCEL + MB_ICONQUESTION); IF rc = MBID_OK THEN StopReceiving (comport - COM_OFF); WinPostMsg (hwnd, WM_QUIT, 0, 0); END; | IDM_COMPORT: WinDlgBox (HWND_DESKTOP, hwnd, ComDlgProc, 0, IDM_COMPORT, 0); SetPort; | IDM_BAUDRATE: WinDlgBox (HWND_DESKTOP, hwnd, BaudDlgProc, 0, IDM_BAUDRATE, 0); SetPort; | IDM_DATABITS: WinDlgBox (HWND_DESKTOP, hwnd, DataDlgProc, 0, IDM_DATABITS, 0); SetPort; | IDM_STOPBITS: WinDlgBox (HWND_DESKTOP, hwnd, StopDlgProc, 0, IDM_STOPBITS, 0); SetPort; | IDM_PARITY: WinDlgBox (HWND_DESKTOP, hwnd, ParityDlgProc, 0, IDM_PARITY, 0); SetPort; | IDM_WHITE: UnCheck (ColorSet); ColorSet := IDM_WHITE; Check (ColorSet); White; | IDM_GREEN: UnCheck (ColorSet); ColorSet := IDM_GREEN; Check (ColorSet); Green; | IDM_AMBER: UnCheck (ColorSet); ColorSet := IDM_AMBER; Check (ColorSet); Amber; | IDM_C1: UnCheck (ColorSet); ColorSet := IDM_C1; Check (ColorSet); Color1; | IDM_C2: UnCheck (ColorSet); ColorSet := IDM_C2; Check (ColorSet); Color2; | IDM_ABOUT: WinDlgBox (HWND_DESKTOP, hwnd, AboutDlgProc, 0, IDM_ABOUT, 0); ELSE (* Don't do anything... *) END; END DoMenu; PROCEDURE ComDlgProc ['ComDlgProc'] ( (* Process Dialog Box for choosing COM1/COM2 *) hwnd : HWND; msg : USHORT; mp1 : MPARAM; mp2 : MPARAM) : MRESULT [LONG, LOADDS]; BEGIN CASE msg OF WM_INITDLG: WinSendDlgItemMsg (hwnd, comport, BM_SETCHECK, 1, 0); WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, comport)); RETURN 1; | WM_CONTROL: comport := LOWORD (mp1); RETURN 0; | WM_COMMAND: WinDismissDlg (hwnd, 1); RETURN 0; ELSE RETURN WinDefDlgProc (hwnd, msg, mp1, mp2); END; END ComDlgProc; PROCEDURE BaudDlgProc ['BaudDlgProc'] ( (* Process Dialog Box for choosing Baud Rate *) hwnd : HWND; msg : USHORT; mp1 : MPARAM; mp2 : MPARAM) : MRESULT [LONG, LOADDS]; BEGIN WITH Settings[comport - COM_OFF] DO CASE msg OF WM_INITDLG: WinSendDlgItemMsg (hwnd, baudrate, BM_SETCHECK, 1, 0); WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, baudrate)); RETURN 1; | WM_CONTROL: baudrate := LOWORD (mp1); RETURN 0; | WM_COMMAND: WinDismissDlg (hwnd, 1); RETURN 0; ELSE RETURN WinDefDlgProc (hwnd, msg, mp1, mp2); END; END; END BaudDlgProc; PROCEDURE DataDlgProc ['DataDlgProc'] ( (* Process Dialog Box for choosing 7 or 8 data bits *) hwnd : HWND; msg : USHORT; mp1 : MPARAM; mp2 : MPARAM) : MRESULT [LONG, LOADDS]; BEGIN WITH Settings[comport - COM_OFF] DO CASE msg OF WM_INITDLG: WinSendDlgItemMsg (hwnd, databits, BM_SETCHECK, 1, 0); WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, databits)); RETURN 1; | WM_CONTROL: databits := LOWORD (mp1); RETURN 0; | WM_COMMAND: WinDismissDlg (hwnd, 1); RETURN 0; ELSE RETURN WinDefDlgProc (hwnd, msg, mp1, mp2); END; END; END DataDlgProc; PROCEDURE StopDlgProc ['StopDlgProc'] ( (* Process Dialog Box for choosing 1 or 2 stop bits *) hwnd : HWND; msg : USHORT; mp1 : MPARAM; mp2 : MPARAM) : MRESULT [LONG, LOADDS]; BEGIN WITH Settings[comport - COM_OFF] DO CASE msg OF WM_INITDLG: WinSendDlgItemMsg (hwnd, stopbits, BM_SETCHECK, 1, 0); WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, stopbits)); RETURN 1; | WM_CONTROL: stopbits := LOWORD (mp1); RETURN 0; | WM_COMMAND: WinDismissDlg (hwnd, 1); RETURN 0; ELSE RETURN WinDefDlgProc (hwnd, msg, mp1, mp2); END; END; END StopDlgProc; PROCEDURE ParityDlgProc ['ParityDlgProc'] ( (* Process Dialog Box for choosing odd, even, or no parity *) hwnd : HWND; msg : USHORT; mp1 : MPARAM; mp2 : MPARAM) : MRESULT [LONG, LOADDS]; BEGIN WITH Settings[comport - COM_OFF] DO CASE msg OF WM_INITDLG: WinSendDlgItemMsg (hwnd, parity, BM_SETCHECK, 1, 0); WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, parity)); RETURN 1; | WM_CONTROL: parity := LOWORD (mp1); RETURN 0; | WM_COMMAND: WinDismissDlg (hwnd, 1); RETURN 0; ELSE RETURN WinDefDlgProc (hwnd, msg, mp1, mp2); END; END; END ParityDlgProc; PROCEDURE AboutDlgProc ['AboutDlgProc'] ( (* Process "About" Dialog Box *) hwnd : HWND; msg : USHORT; mp1 : MPARAM; mp2 : MPARAM) : MRESULT [LONG, LOADDS]; BEGIN IF msg = WM_COMMAND THEN WinDismissDlg (hwnd, 1); RETURN 0; ELSE RETURN WinDefDlgProc (hwnd, msg, mp1, mp2); END; END AboutDlgProc; PROCEDURE SendFNDlgProc ['SendFNDlgProc'] ( (* Process Dialog Box that obtains send filename from user *) hwnd : HWND; msg : USHORT; mp1 : MPARAM; mp2 : MPARAM) : MRESULT [LONG, LOADDS]; BEGIN CASE msg OF WM_INITDLG: WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, ID_SENDFN)); RETURN 1; | WM_COMMAND: WinQueryDlgItemText (hwnd, ID_SENDFN, 20, ADR (sFname)); WinDismissDlg (hwnd, 1); RETURN 0; ELSE RETURN WinDefDlgProc (hwnd, msg, mp1, mp2); END; END SendFNDlgProc; PROCEDURE PathDlgProc ['PathDlgProc'] ( (* Process Dialog Box that obtains directory path from user *) hwnd : HWND; msg : USHORT; mp1 : MPARAM; mp2 : MPARAM) : MRESULT [LONG, LOADDS]; BEGIN CASE msg OF WM_INITDLG: WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, ID_DIRPATH)); RETURN 1; | WM_COMMAND: WinQueryDlgItemText (hwnd, ID_DIRPATH, 60, ADR (Path)); WinDismissDlg (hwnd, 1); RETURN 0; ELSE RETURN WinDefDlgProc (hwnd, msg, mp1, mp2); END; END PathDlgProc; PROCEDURE DirEndDlgProc ['DirEndDlgProc'] ( (* Process Dialog Box to allow user to cancel directory *) hwnd : HWND; msg : USHORT; mp1 : MPARAM; mp2 : MPARAM) : MRESULT [LONG, LOADDS]; BEGIN IF msg = WM_COMMAND THEN WinDismissDlg (hwnd, 1); RETURN 0; ELSE RETURN WinDefDlgProc (hwnd, msg, mp1, mp2); END; END DirEndDlgProc; PROCEDURE HelpDlgProc ['HelpDlgProc'] ( (* Process Dialog Boxes for the HELP *) hwnd : HWND; msg : USHORT; mp1 : MPARAM; mp2 : MPARAM) : MRESULT [LONG, LOADDS]; BEGIN IF msg = WM_COMMAND THEN WinDismissDlg (hwnd, 1); RETURN 0; ELSE RETURN WinDefDlgProc (hwnd, msg, mp1, mp2); END; END HelpDlgProc; PROCEDURE KeyTranslate (mp1, mp2 : MPARAM; VAR c1, c2 : CHAR) : BOOLEAN; (* Translates WM_CHAR message into ascii keystroke *) VAR code : CARDINAL; fs : BITSET; VK, KU, CH, CT : BOOLEAN; BEGIN fs := BITSET (LOWORD (mp1)); (* flags *) VK := (fs * BITSET (KC_VIRTUALKEY)) # {}; KU := (fs * BITSET (KC_KEYUP)) # {}; CH := (fs * BITSET (KC_CHAR)) # {}; CT := (fs * BITSET (KC_CTRL)) # {}; IF (NOT KU) THEN code := LOWORD (mp2); (* character code *) c1 := CHR (code); c2 := CHR (code DIV 256); IF ORD (c1) = 0E0H THEN (* function *) c1 := 0C; END; IF CT AND (NOT CH) AND (NOT VK) AND (code # 0) THEN c1 := CHR (CARDINAL ((BITSET (ORD (c1)) * BITSET (1FH)))); END; RETURN TRUE; ELSE RETURN FALSE; END; END KeyTranslate; PROCEDURE WindowProc ['WindowProc'] ( (* Main Window Procedure -- Handles message from PM and elsewhere *) hwnd : HWND; msg : USHORT; mp1 : MPARAM; mp2 : MPARAM) : MRESULT [LONG, LOADDS]; VAR ch : CHAR; hps : HPS; pswp : PSWP; c1, c2 : CHAR; BEGIN CASE msg OF WM_HELP: IF TermMode THEN WinDlgBox (HWND_DESKTOP, hwnd, HelpDlgProc, 0, IDM_TERMHELP, 0); ELSE WinDlgBox (HWND_DESKTOP, hwnd, HelpDlgProc, 0, IDM_HELPMENU, 0); END; RETURN 0; | WM_SETFULL: SetFull; RETURN 0; | WM_SETRESTORE: SetRestore; RETURN 0; | WM_SETMAX: SetMax; RETURN 0; | WM_MINMAXFRAME: pswp := PSWP (mp1); IF BITSET (pswp^.fs) * BITSET (SWP_MINIMIZE) # {} THEN (* Don't Display Port Settings While Minimized *) WinSetWindowText (FrameWindow, ADR (Title)); ELSE WinSetWindowText (FrameWindow, ADR (Banner)); IF TermMode AND (BITSET (pswp^.fs) * BITSET (SWP_RESTORE) # {}) THEN (* Force window to be maximized in terminal mode *) WinPostMsg (FrameWindow, WM_SETMAX, 0, 0); ELSIF (NOT TermMode) AND (BITSET (pswp^.fs) * BITSET (SWP_MAXIMIZE) # {}) THEN (* Prevent maximized window EXCEPT in terminal mode *) WinPostMsg (FrameWindow, WM_SETRESTORE, 0, 0); ELSE (* Do Nothing *) END; END; RETURN WinDefWindowProc (hwnd, msg, mp1, mp2); | WM_CREATE: hdc := WinOpenWindowDC (hwnd); VioCreatePS (frame_hvps, 25, 80, 0, FORMAT_CGA, 0); VioCreatePS (child_hvps, 16, 40, 0, FORMAT_CGA, 0); DosCreateThread (TermThrProc, TermThr, ADR (TermStack[STKSIZE])); DosSuspendThread (TermThr); RETURN 0; | WM_INITMENU: Check (ColorSet); RETURN 0; | WM_COMMAND: DoMenu (hwnd, mp1); RETURN 0; | WM_TERMQUIT: TermMode := FALSE; DosSuspendThread (TermThr); VioAssociate (0, hvps); (* Restore The Window *) SetRestore; Enable (IDM_CONNECT); Enable (IDM_SEND); Enable (IDM_REC); Enable (IDM_DIR); Enable (IDM_OPTIONS); Enable (IDM_COLORS); RETURN 0; | WM_TERM: PutPortChar (CHR (LOWORD (mp1))); (* To Screen *) RETURN 0; | WM_CHAR: IF TermMode THEN IF KeyTranslate (mp1, mp2, c1, c2) THEN PutKbdChar (c1, c2); (* To Port *) RETURN 0; ELSE RETURN WinDefAVioWindowProc (hwnd, msg, mp1, mp2); END; ELSE RETURN WinDefWindowProc (hwnd, msg, mp1, mp2); END; | WM_PAINT: hps := WinBeginPaint (hwnd, NULL, ADDRESS (NULL)); GpiErase (hps); VioShowPS (25, 80, 0, hvps); WinEndPaint (hps); RETURN 0; | WM_SIZE: IF TermMode THEN RETURN WinDefAVioWindowProc (hwnd, msg, mp1, mp2); ELSE RETURN WinDefWindowProc (hwnd, msg, mp1, mp2); END; | WM_DESTROY: VioDestroyPS (frame_hvps); VioDestroyPS (child_hvps); RETURN 0; ELSE RETURN WinDefWindowProc (hwnd, msg, mp1, mp2); END; END WindowProc; PROCEDURE ChildWindowProc ['ChildWindowProc'] ( (* Window Procedure for Send/Receive child windows *) hwnd : HWND; msg : USHORT; mp1 : MPARAM; mp2 : MPARAM) : MRESULT [LONG, LOADDS]; VAR mp : USHORT; hps : HPS; c1, c2 : CHAR; BEGIN CASE msg OF WM_PAINT: hps := WinBeginPaint (hwnd, NULL, ADDRESS (NULL)); GpiErase (hps); VioShowPS (16, 40, 0, hvps); WinEndPaint (hps); RETURN 0; | WM_CHAR: IF KeyTranslate (mp1, mp2, c1, c2) AND (c1 = ESC) THEN Aborted := TRUE; RETURN 0; ELSE RETURN WinDefWindowProc (hwnd, msg, mp1, mp2); END; | WM_PAD: mp := LOWORD (mp1); IF (mp = PAD_Error) OR (mp = PAD_Quit) THEN WriteLn; IF mp = PAD_Error THEN WinMessageBox (HWND_DESKTOP, hwnd, ADR ("File Transfer Aborted"), ADR (Class), 0, MB_OK + MB_ICONEXCLAMATION); ELSE WinMessageBox (HWND_DESKTOP, hwnd, ADR ("File Transfer Completed"), ADR (Class), 0, MB_OK + MB_ICONASTERISK); END; DosSleep (2000); VioAssociate (0, hvps); WinDestroyWindow(ChildFrameWindow); Enable (IDM_CONNECT); Enable (IDM_SEND); Enable (IDM_REC); Enable (IDM_DIR); Enable (IDM_OPTIONS); Enable (IDM_COLORS); ELSE DoPADMsg (mp1, mp2); END; RETURN 0; | WM_DL: DoDLMsg (mp1, mp2); RETURN 0; | WM_SIZE: WinSetWindowPos (ChildFrameWindow, 0, Pos.cx DIV 4, Pos.cy DIV 4, Pos.cx DIV 2, Pos.cy DIV 2 - 3, SWP_MOVE + SWP_SIZE); RETURN WinDefAVioWindowProc (hwnd, msg, mp1, mp2); ELSE RETURN WinDefWindowProc (hwnd, msg, mp1, mp2); END; END ChildWindowProc; BEGIN (* Module Initialization *) WITH Settings[ID_COM1 - COM_OFF] DO baudrate := ID_B1200; parity := ID_EVEN; databits := ID_DATA7; stopbits := ID_STOP1; END; WITH Settings[ID_COM2 - COM_OFF] DO baudrate := ID_B19K2; parity := ID_EVEN; databits := ID_DATA7; stopbits := ID_STOP1; END; PrevComPort := NONE; comport := ID_COM1; TermMode := FALSE; (* Not Initially in Terminal Emulation Mode *) END Shell. [LISTING TEN] IMPLEMENTATION MODULE Term; (* TVI950 Terminal Emulation for Kermit *) FROM Drives IMPORT SetDrive; FROM Directories IMPORT FileAttributes, AttributeSet, DirectoryEntry, FindFirst, FindNext; FROM SYSTEM IMPORT ADR; FROM OS2DEF IMPORT ULONG; FROM DosCalls IMPORT DosChDir, DosSleep; FROM Screen IMPORT ClrScr, ClrEol, GotoXY, GetXY, Right, Left, Up, Down, WriteAtt, WriteString, WriteLn, Write, attribute, NORMAL, HIGHLIGHT, REVERSE; FROM PMWIN IMPORT WinPostMsg, MPFROM2SHORT; FROM Shell IMPORT comport, FrameWindow; FROM KH IMPORT COM_OFF; FROM CommPort IMPORT CommStatus, GetChar, SendChar; FROM Strings IMPORT Length, Concat; IMPORT ASCII; CONST (* Key codes: Note: F1 -- F12 are actually Shift-F1 -- Shift-F12 *) F1 = 124C; F2 = 125C; F3 = 126C; F4 = 127C; F5 = 130C; F6 = 131C; F7 = 132C; F8 = 133C; F9 = 134C; F10 = 135C; F11 = 207C; F12 = 210C; AF1 = 213C; (* Alt-F1 *) AF2 = 214C; (* Alt-F2 *) INS = 122C; DEL = 123C; HOME = 107C; PGDN = 121C; (* synonym for PF10 *) PGUP = 111C; (* synonym for PF11 *) ENDD = 117C; (* synonym for PF12 *) UPARROW = 110C; DOWNARROW = 120C; LEFTARROW = 113C; RIGHTARROW = 115C; CtrlX = 30C; CtrlCaret = 36C; CtrlZ = 32C; CtrlL = 14C; CtrlH = 10C; CtrlK = 13C; CtrlJ = 12C; CtrlV = 26C; ESC = 33C; BUFSIZE = 4096; (* character buffer used by term thread *) VAR commStat : CommStatus; echo : (Off, Local, On); newline: BOOLEAN; (* translate to *) Insert : BOOLEAN; PROCEDURE Dir (path : ARRAY OF CHAR); (* Change drive and/or directory; display a directory (in wide format) *) VAR gotFN : BOOLEAN; filename : ARRAY [0..20] OF CHAR; attr : AttributeSet; ent : DirectoryEntry; i, j, k : INTEGER; BEGIN filename := ""; (* in case no directory change *) i := Length (path); IF (i > 2) AND (path[1] = ':') THEN (* drive specifier *) DEC (i, 2); SetDrive (ORD (CAP (path[0])) - ORD ('A')); FOR j := 0 TO i DO (* strip off the drive specifier *) path[j] := path[j + 2]; END; END; IF i # 0 THEN gotFN := FALSE; WHILE (i >= 0) AND (path[i] # '\') DO IF path[i] = '.' THEN gotFN := TRUE; END; DEC (i); END; IF gotFN THEN j := i + 1; k := 0; WHILE path[j] # 0C DO filename[k] := path[j]; INC (k); INC (j); END; filename[k] := 0C; IF (i = -1) OR ((i = 0) AND (path[0] = '\')) THEN INC (i); END; path[i] := 0C; END; END; IF Length (path) # 0 THEN DosChDir (ADR (path), 0); END; IF Length (filename) = 0 THEN filename := "*.*"; END; attr := AttributeSet {ReadOnly, Directory, Archive}; i := 1; (* keep track of position on line *) ClrScr; gotFN := FindFirst (filename, attr, ent); WHILE gotFN DO WriteString (ent.name); j := Length (ent.name); WHILE j < 12 DO (* 12 is maximum length for "filename.typ" *) Write (' '); INC (j); END; INC (i); (* next position on this line *) IF i > 5 THEN i := 1; (* start again on new line *) WriteLn; ELSE WriteString (" | "); END; gotFN := FindNext (ent); END; WriteLn; END Dir; PROCEDURE InitTerm; (* Clear Screen, Home Cursor, Get Ready For Terminal Emulation *) BEGIN ClrScr; Insert := FALSE; attribute := NORMAL; END InitTerm; PROCEDURE PutKbdChar (ch1, ch2 : CHAR); (* Process a character received from the keyboard *) BEGIN IF ch1 = ASCII.enq THEN (* Control-E *) echo := On; ELSIF ch1 = ASCII.ff THEN (* Control-L *) echo := Local; ELSIF ch1 = ASCII.dc4 THEN (* Control-T *) echo := Off; ELSIF ch1 = ASCII.so THEN (* Control-N *) newline := TRUE; ELSIF ch1 = ASCII.si THEN (* Control-O *) newline := FALSE; ELSIF (ch1 = ASCII.can) OR (ch1 = ESC) THEN attribute := NORMAL; WinPostMsg (FrameWindow, WM_TERMQUIT, 0, 0); ELSIF ch1 = 0C THEN Function (ch2); ELSE commStat := SendChar (comport - COM_OFF, ch1, FALSE); IF (echo = On) OR (echo = Local) THEN WriteAtt (ch1); END; END; END PutKbdChar; PROCEDURE Function (ch : CHAR); (* handles the function keys -- including PF1 - PF12, etc. *) BEGIN CASE ch OF F1 : commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE); commStat := SendChar (comport - COM_OFF, '@', FALSE); commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE); | F2 : commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE); commStat := SendChar (comport - COM_OFF, 'A', FALSE); commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE); | F3 : commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE); commStat := SendChar (comport - COM_OFF, 'B', FALSE); commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE); | F4 : commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE); commStat := SendChar (comport - COM_OFF, 'C', FALSE); commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE); | F5 : commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE); commStat := SendChar (comport - COM_OFF, 'D', FALSE); commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE); | F6 : commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE); commStat := SendChar (comport - COM_OFF, 'E', FALSE); commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE); | F7 : commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE); commStat := SendChar (comport - COM_OFF, 'F', FALSE); commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE); | F8 : commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE); commStat := SendChar (comport - COM_OFF, 'G', FALSE); commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE); | F9 : commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE); commStat := SendChar (comport - COM_OFF, 'H', FALSE); commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE); | F10, PGDN: commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE); commStat := SendChar (comport - COM_OFF, 'I', FALSE); commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE); | F11, AF1, PGUP: commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE); commStat := SendChar (comport - COM_OFF, 'J', FALSE); commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE); | F12, AF2, ENDD: commStat := SendChar (comport - COM_OFF, ESC, FALSE); commStat := SendChar (comport - COM_OFF, 'Q', FALSE); | INS : IF NOT Insert THEN commStat := SendChar (comport - COM_OFF, ESC, FALSE); commStat := SendChar (comport - COM_OFF, 'E', FALSE); END; | DEL : commStat := SendChar (comport - COM_OFF, ESC, FALSE); commStat := SendChar (comport - COM_OFF, 'R', FALSE); | HOME : commStat := SendChar (comport - COM_OFF, CtrlZ, FALSE); | UPARROW : commStat := SendChar (comport - COM_OFF, CtrlK, FALSE); | DOWNARROW : commStat := SendChar (comport - COM_OFF, CtrlV, FALSE); | LEFTARROW : commStat := SendChar (comport - COM_OFF, CtrlH, FALSE); | RIGHTARROW : commStat := SendChar (comport - COM_OFF, CtrlL, FALSE); ELSE (* do nothing *) END; END Function; PROCEDURE TermThrProc; (* Thread to get characters from port, put into buffer *) VAR ch : CHAR; BEGIN LOOP IF GetChar (comport - COM_OFF, ch) = Success THEN WinPostMsg (FrameWindow, WM_TERM, MPFROM2SHORT (ORD (ch), 0), 0); ELSE DosSleep (0); END END; END TermThrProc; VAR EscState, CurState1, CurState2 : BOOLEAN; CurChar1 : CHAR; PROCEDURE PutPortChar (ch : CHAR); (* Process a character received from the port *) BEGIN IF EscState THEN EscState := FALSE; IF ch = '=' THEN CurState1 := TRUE; ELSE Escape (ch); END; ELSIF CurState1 THEN CurState1 := FALSE; CurChar1 := ch; CurState2 := TRUE; ELSIF CurState2 THEN CurState2 := FALSE; Cursor (ch); ELSE CASE ch OF CtrlCaret, CtrlZ : ClrScr; | CtrlL : Right; | CtrlH : Left; | CtrlK : Up; | CtrlJ : Down; | ESC : EscState := TRUE; ELSE WriteAtt (ch); IF newline AND (ch = ASCII.cr) THEN WriteLn; END; END; END; IF echo = On THEN commStat := SendChar (comport - COM_OFF, ch, FALSE); END; END PutPortChar; PROCEDURE Escape (ch : CHAR); (* handles escape sequences *) BEGIN CASE ch OF '*' : ClrScr; | 'T', 'R' : ClrEol; | ')' : attribute := NORMAL; | '(' : attribute := HIGHLIGHT; | 'f' : InsertMsg; | 'g' : InsertOn; ELSE (* ignore *) END; END Escape; PROCEDURE Cursor (ch : CHAR); (* handles cursor positioning *) VAR x, y : CARDINAL; BEGIN y := ORD (CurChar1) - 20H; x := ORD (ch) - 20H; GotoXY (x, y); (* adjust for HOME = (1, 1) *) END Cursor; VAR cx, cy : CARDINAL; PROCEDURE InsertMsg; (* get ready insert mode -- place a message at the bottom of the screen *) BEGIN IF NOT Insert THEN GetXY (cx, cy); (* record current position *) GotoXY (1, 24); ClrEol; attribute := REVERSE; ELSE (* exit Insert mode *) GetXY (cx, cy); GotoXY (1, 24); ClrEol; GotoXY (cx, cy); Insert := FALSE; END; END InsertMsg; PROCEDURE InsertOn; (* enter insert mode -- after INSERT MODE message is printed *) BEGIN attribute := NORMAL; GotoXY (cx, cy); Insert := TRUE; END InsertOn; BEGIN (* module initialization *) echo := Off; newline := FALSE; Insert := FALSE; EscState := FALSE; CurState1 := FALSE; CurState2 := FALSE; END Term. [LISTING ELEVEN] IMPLEMENTATION MODULE Screen; (* module to perform "low level" screen functions (via AVIO) *) IMPORT ASCII; FROM SYSTEM IMPORT ADR; FROM Strings IMPORT Length; FROM Conversions IMPORT IntToString; FROM KH IMPORT IDM_GREEN; FROM Vio IMPORT VioSetCurPos, VioGetCurPos, VioScrollUp, VioWrtNCell, VioWrtTTY, VioCell; CONST GREY = 07H; WHITE = 0FH; REV_GY = 70H; GREEN = 02H; LITE_GRN = 0AH; REV_GRN = 20H; AMBER = 06H; LITE_AMB = 0EH; REV_AMB = 60H; RED = 0CH; CY_BK = 0B0H; CY_BL = 0B9H; REV_RD = 0CFH; REV_BL = 9FH; MAGENTA = 05H; VAR (* From Definition Module NORMAL : CARDINAL; HIGHLIGHT : CARDINAL; REVERSE : CARDINAL; attribute : CARDINAL; hvps : HVPS; *) x, y : CARDINAL; bCell : VioCell; PROCEDURE White; (* Sets up colors: Monochrome White *) BEGIN NORMAL := GREY; HIGHLIGHT := WHITE; REVERSE := REV_GY; attribute := NORMAL; END White; PROCEDURE Green; (* Sets up colors: Monochrome Green *) BEGIN NORMAL := GREEN; HIGHLIGHT := LITE_GRN; REVERSE := REV_GRN; attribute := NORMAL; END Green; PROCEDURE Amber; (* Sets up colors: Monochrome Amber *) BEGIN NORMAL := AMBER; HIGHLIGHT := LITE_AMB; REVERSE := REV_AMB; attribute := NORMAL; END Amber; PROCEDURE Color1; (* Sets up colors: Blue, Red, Green *) BEGIN NORMAL := GREEN; HIGHLIGHT := RED; REVERSE := REV_BL; attribute := NORMAL; END Color1; PROCEDURE Color2; (* Sets up colors: Cyan Background; Black, Blue, White-on-Red *) BEGIN NORMAL := CY_BK; HIGHLIGHT := CY_BL; REVERSE := REV_RD; attribute := NORMAL; END Color2; PROCEDURE HexToString (num : INTEGER; size : CARDINAL; VAR buf : ARRAY OF CHAR; VAR I : CARDINAL; VAR Done : BOOLEAN); (* Local Procedure to convert a number to a string, represented in HEX *) CONST ZERO = 30H; (* ASCII code *) A = 41H; VAR i : CARDINAL; h : CARDINAL; t : ARRAY [0..10] OF CHAR; BEGIN i := 0; REPEAT h := num MOD 16; IF h <= 9 THEN t[i] := CHR (h + ZERO); ELSE t[i] := CHR (h - 10 + A); END; INC (i); num := num DIV 16; UNTIL num = 0; IF (size > HIGH (buf)) OR (i > HIGH (buf)) THEN Done := FALSE; RETURN; ELSE Done := TRUE; END; WHILE size > i DO buf[I] := '0'; (* pad with zeros *) DEC (size); INC (I); END; WHILE i > 0 DO DEC (i); buf[I] := t[i]; INC (I); END; buf[I] := 0C; END HexToString; PROCEDURE ClrScr; (* Clear the screen, and home the cursor *) BEGIN bCell.ch := ' '; (* space = blank screen *) bCell.attr := CHR (NORMAL); (* Normal Video Attribute *) VioScrollUp (0, 0, 24, 79, 25, bCell, hvps); GotoXY (0, 0); END ClrScr; PROCEDURE ClrEol; (* clear from the current cursor position to the end of the line *) BEGIN GetXY (x, y); (* current cursor position *) bCell.ch := ' '; (* space = blank *) bCell.attr := CHR (NORMAL); (* Normal Video Attribute *) VioScrollUp (y, x, y, 79, 1, bCell, hvps); END ClrEol; PROCEDURE Right; (* move cursor to the right *) BEGIN GetXY (x, y); INC (x); GotoXY (x, y); END Right; PROCEDURE Left; (* move cursor to the left *) BEGIN GetXY (x, y); DEC (x); GotoXY (x, y); END Left; PROCEDURE Up; (* move cursor up *) BEGIN GetXY (x, y); DEC (y); GotoXY (x, y); END Up; PROCEDURE Down; (* move cursor down *) BEGIN GetXY (x, y); INC (y); GotoXY (x, y); END Down; PROCEDURE GotoXY (col, row : CARDINAL); (* position cursor at column, row *) BEGIN IF (col <= 79) AND (row <= 24) THEN VioSetCurPos (row, col, hvps); END; END GotoXY; PROCEDURE GetXY (VAR col, row : CARDINAL); (* determine current cursor position *) BEGIN VioGetCurPos (row, col, hvps); END GetXY; PROCEDURE Write (c : CHAR); (* Write a Character *) BEGIN WriteAtt (c); END Write; PROCEDURE WriteString (str : ARRAY OF CHAR); (* Write String *) VAR i : CARDINAL; c : CHAR; BEGIN i := 0; c := str[i]; WHILE c # 0C DO Write (c); INC (i); c := str[i]; END; END WriteString; PROCEDURE WriteInt (n : INTEGER; s : CARDINAL); (* Write Integer *) VAR i : CARDINAL; b : BOOLEAN; str : ARRAY [0..6] OF CHAR; BEGIN i := 0; IntToString (n, s, str, i, b); WriteString (str); END WriteInt; PROCEDURE WriteHex (n, s : CARDINAL); (* Write a Hexadecimal Number *) VAR i : CARDINAL; b : BOOLEAN; str : ARRAY [0..6] OF CHAR; BEGIN i := 0; HexToString (n, s, str, i, b); WriteString (str); END WriteHex; PROCEDURE WriteLn; (* Write *) BEGIN Write (ASCII.cr); Write (ASCII.lf); END WriteLn; PROCEDURE WriteAtt (c : CHAR); (* write character and attribute at cursor position *) VAR s : ARRAY [0..1] OF CHAR; BEGIN GetXY (x, y); IF (c = ASCII.ht) THEN bCell.ch := ' '; bCell.attr := CHR (attribute); REPEAT VioWrtNCell (bCell, 1, y, x, hvps); Right; UNTIL (x MOD 8) = 0; ELSIF (c = ASCII.cr) OR (c = ASCII.lf) OR (c = ASCII.bel) OR (c = ASCII.bs) THEN s[0] := c; s[1] := 0C; VioWrtTTY (ADR (s), 1, hvps); IF c = ASCII.lf THEN ClrEol; END; ELSE bCell.ch := c; bCell.attr := CHR (attribute); VioWrtNCell (bCell, 1, y, x, hvps); Right; END; END WriteAtt; BEGIN (* module initialization *) ColorSet := IDM_GREEN; NORMAL := GREEN; HIGHLIGHT := LITE_GRN; REVERSE := REV_GRN; attribute := NORMAL; END Screen. [LISTING TWELVE] (**************************************************************************) (* *) (* Copyright (c) 1988, 1989 *) (* by Stony Brook Software *) (* and *) (* Copyright (c) 1990 *) (* by Brian R. Anderson *) (* All rights reserved. *) (* *) (**************************************************************************) IMPLEMENTATION MODULE CommPort [7]; FROM SYSTEM IMPORT ADR, BYTE, WORD, ADDRESS; FROM Storage IMPORT ALLOCATE, DEALLOCATE; FROM DosCalls IMPORT DosOpen, AttributeSet, DosDevIOCtl, DosClose, DosRead, DosWrite; TYPE CP = POINTER TO CHAR; VAR pn : CARDINAL; Handle : ARRAY [0..3] OF CARDINAL; BufIn : ARRAY [0..3] OF CP; BufOut : ARRAY [0..3] OF CP; BufStart : ARRAY [0..3] OF CP; BufLimit : ARRAY [0..3] OF CP; BufSize : ARRAY [0..3] OF CARDINAL; Temp : ARRAY [1..1024] OF CHAR; (* size of OS/2's serial queue *) PROCEDURE CheckPort (portnum : CARDINAL) : BOOLEAN; (* Check for a valid port number and open the port if it not alredy open *) CONST PortName : ARRAY [0..3] OF ARRAY [0..4] OF CHAR = [['COM1', 0C], ['COM2', 0C], ['COM3', 0C], ['COM4', 0C]]; VAR Action : CARDINAL; BEGIN (* check the port number *) IF portnum > 3 THEN RETURN FALSE; END; (* attempt to open the port if it is not already open *) IF Handle[portnum] = 0 THEN IF DosOpen(ADR(PortName[portnum]), Handle[portnum], Action, 0, AttributeSet{}, 1, 12H, 0) # 0 THEN RETURN FALSE; END; END; RETURN TRUE; END CheckPort; PROCEDURE InitPort (portnum : CARDINAL; speed : BaudRate; data : DataBits; stop : StopBits; check : Parity) : CommStatus; (* Initialize a port *) CONST Rate : ARRAY BaudRate OF CARDINAL = [110, 150, 300, 600, 1200, 2400, 4800, 9600, 19200]; TransParity : ARRAY Parity OF BYTE = [2, 1, 0]; TYPE LineChar = RECORD bDataBits : BYTE; bParity : BYTE; bStopBits : BYTE; END; VAR LC : LineChar; BEGIN (* Check the port number *) IF NOT CheckPort(portnum) THEN RETURN InvalidPort; END; (* Set the baud rate *) IF DosDevIOCtl(0, ADR(Rate[speed]), 41H, 1, Handle[portnum]) # 0 THEN RETURN InvalidParameter; END; (* set the characteristics *) LC.bDataBits := BYTE(data); IF stop = 1 THEN DEC (stop); (* 0x00 = 1 stop bits; 0x02 = 2 stop bits *) END; LC.bStopBits := BYTE(stop); LC.bParity := TransParity[check]; IF DosDevIOCtl(0, ADR(LC), 42H, 1, Handle[portnum]) # 0 THEN RETURN InvalidParameter; END; RETURN Success; END InitPort; PROCEDURE StartReceiving (portnum, bufsize : CARDINAL) : CommStatus; (* Start receiving characters on a port *) BEGIN IF NOT CheckPort(portnum) THEN RETURN InvalidPort; END; IF BufStart[portnum] # NIL THEN RETURN AlreadyReceiving; END; ALLOCATE (BufStart[portnum], bufsize); BufIn[portnum] := BufStart[portnum]; BufOut[portnum] := BufStart[portnum]; BufLimit[portnum] := BufStart[portnum]; INC (BufLimit[portnum]:ADDRESS, bufsize - 1); BufSize[portnum] := bufsize; RETURN Success; END StartReceiving; PROCEDURE StopReceiving (portnum : CARDINAL) : CommStatus; (* Stop receiving characters on a port *) BEGIN IF NOT CheckPort(portnum) THEN RETURN InvalidPort; END; IF BufStart[portnum] # NIL THEN DEALLOCATE (BufStart[portnum], BufSize[portnum]); BufLimit[portnum] := NIL; BufIn[portnum] := NIL; BufOut[portnum] := NIL; BufSize[portnum] := 0; END; DosClose(Handle[portnum]); Handle[portnum] := 0; RETURN Success; END StopReceiving; PROCEDURE GetChar (portnum : CARDINAL; VAR ch : CHAR) : CommStatus; (* Get a character from the comm port *) VAR status : CARDINAL; read : CARDINAL; que : RECORD ct : CARDINAL; sz : CARDINAL; END; i : CARDINAL; BEGIN IF BufStart[portnum] = NIL THEN RETURN NotReceiving; END; IF NOT CheckPort(portnum) THEN RETURN InvalidPort; END; status := DosDevIOCtl (ADR (que), 0, 68H, 1, Handle[portnum]); IF (status = 0) AND (que.ct # 0) THEN status := DosRead (Handle[portnum], ADR (Temp), que.ct, read); IF (status # 0) OR (read = 0) THEN RETURN NotReceiving; END; FOR i := 1 TO read DO BufIn[portnum]^ := Temp[i]; IF BufIn[portnum] = BufLimit[portnum] THEN BufIn[portnum] := BufStart[portnum]; ELSE INC (BufIn[portnum]:ADDRESS); END; IF BufIn[portnum] = BufOut[portnum] THEN RETURN BufferOverflow; END; END; END; IF BufIn[portnum] = BufOut[portnum] THEN RETURN NoCharacter; END; ch := BufOut[portnum]^; IF BufOut[portnum] = BufLimit[portnum] THEN BufOut[portnum] := BufStart[portnum]; ELSE INC (BufOut[portnum]:ADDRESS); END; RETURN Success; END GetChar; PROCEDURE SendChar (portnum : CARDINAL; ch : CHAR; modem : BOOLEAN) : CommStatus; (* send a character to the comm port *) VAR wrote : CARDINAL; status : CARDINAL; commSt : CHAR; BEGIN IF NOT CheckPort(portnum) THEN RETURN InvalidPort; END; status := DosDevIOCtl (ADR (commSt), 0, 64H, 1, Handle[portnum]); IF (status # 0) OR (commSt # 0C) THEN RETURN TimeOut; ELSE status := DosWrite(Handle[portnum], ADR(ch), 1, wrote); IF (status # 0) OR (wrote # 1) THEN RETURN TimeOut; ELSE RETURN Success; END; END; END SendChar; BEGIN (* module initialization *) (* nothing open yet *) FOR pn := 0 TO 3 DO Handle[pn] := 0; BufStart[pn] := NIL; BufLimit[pn] := NIL; BufIn[pn] := NIL; BufOut[pn] := NIL; BufSize[pn] := 0; END; END CommPort. [LISTING THIRTEEN] IMPLEMENTATION MODULE Files; (* File I/O for Kermit *) FROM FileSystem IMPORT File, Response, Delete, Lookup, Close, ReadNBytes, WriteNBytes; FROM Strings IMPORT Append; FROM Conversions IMPORT CardToString; FROM SYSTEM IMPORT ADR, SIZE; TYPE buffer = ARRAY [1..512] OF CHAR; VAR ext : CARDINAL; (* new file extensions to avoid name conflict *) inBuf, outBuf : buffer; inP, outP : CARDINAL; (* buffer pointers *) read, written : CARDINAL; (* number of bytes read or written *) (* by ReadNBytes or WriteNBytes *) PROCEDURE Open (VAR f : File; name : ARRAY OF CHAR) : Status; (* opens an existing file for reading, returns status *) BEGIN Lookup (f, name, FALSE); IF f.res = done THEN inP := 0; read := 0; RETURN Done; ELSE RETURN Error; END; END Open; PROCEDURE Create (VAR f : File; name : ARRAY OF CHAR) : Status; (* creates a new file for writing, returns status *) VAR ch : CHAR; str : ARRAY [0..3] OF CHAR; i : CARDINAL; b : BOOLEAN; BEGIN LOOP Lookup (f, name, FALSE); (* check to see if file exists *) IF f.res = done THEN Close (f); (* Filename Clash: Change file name *) IF ext > 99 THEN (* out of new names... *) RETURN Error; END; i := 0; WHILE (name[i] # 0C) AND (name[i] # '.') DO INC (i); (* scan for end of filename *) END; name[i] := '.'; name[i + 1] := 'K'; name[i + 2] := 0C; i := 0; CardToString (ext, 1, str, i, b); Append (name, str); (* append new extension *) INC (ext); ELSE EXIT; END; END; Lookup (f, name, TRUE); IF f.res = done THEN outP := 0; RETURN Done; ELSE RETURN Error; END; END Create; PROCEDURE CloseFile (VAR f : File; Which : FileType) : Status; (* closes a file after reading or writing *) BEGIN written := outP; IF (Which = Output) AND (outP > 0) THEN WriteNBytes (f, ADR (outBuf), outP); written := f.count; END; Close (f); IF (written = outP) AND (f.res = done) THEN RETURN Done; ELSE RETURN Error; END; END CloseFile; PROCEDURE Get (VAR f : File; VAR ch : CHAR) : Status; (* Reads one character from the file, returns status *) BEGIN IF inP = read THEN ReadNBytes (f, ADR (inBuf), SIZE (inBuf)); read := f.count; inP := 0; END; IF read = 0 THEN RETURN EOF; ELSE INC (inP); ch := inBuf[inP]; RETURN Done; END; END Get; PROCEDURE Put (ch : CHAR); (* Writes one character to the file buffer *) BEGIN INC (outP); outBuf[outP] := ch; END Put; PROCEDURE DoWrite (VAR f : File) : Status; (* Writes buffer to disk only if nearly full *) BEGIN IF outP < 400 THEN (* still room in buffer *) RETURN Done; ELSE WriteNBytes (f, ADR (outBuf), outP); written := f.count; IF (written = outP) AND (f.res = done) THEN outP := 0; RETURN Done; ELSE RETURN Error; END; END; END DoWrite; BEGIN (* module initialization *) ext := 0; END Files. [LISTING FOURTEEN] DEFINITION MODULE KH; CONST ID_OK = 25; PARITY_OFF = 150; ID_NONE = 152; ID_ODD = 151; ID_EVEN = 150; STOP_OFF = 140; ID_STOP2 = 142; ID_STOP1 = 141; DATA_OFF = 130; ID_DATA8 = 138; ID_DATA7 = 137; BAUD_OFF = 120; ID_B19K2 = 128; ID_B9600 = 127; ID_B4800 = 126; ID_B2400 = 125; ID_B1200 = 124; ID_B600 = 123; ID_B300 = 122; ID_B150 = 121; ID_B110 = 120; COM_OFF = 100; ID_COM2 = 101; ID_COM1 = 100; IDM_C2 = 24; IDM_C1 = 23; IDM_AMBER = 22; IDM_GREEN = 21; IDM_WHITE = 20; IDM_COLORS = 19; IDM_DIREND = 18; ID_DIRPATH = 17; ID_SENDFN = 16; IDM_DIRPATH = 15; IDM_SENDFN = 14; IDM_TERMHELP = 13; IDM_HELPMENU = 12; IDM_ABOUT = 11; IDM_PARITY = 10; IDM_STOPBITS = 9; IDM_DATABITS = 8; IDM_BAUDRATE = 7; IDM_COMPORT = 6; IDM_QUIT = 5; IDM_REC = 4; IDM_SEND = 3; IDM_CONNECT = 2; IDM_DIR = 1; IDM_OPTIONS = 52; IDM_FILE = 51; IDM_KERMIT = 50; END KH. [LISTING FIFTEEN] IMPLEMENTATION MODULE KH; END KH. [LISTING SIXTEEN] #define IDM_KERMIT 50 #define IDM_FILE 51 #define IDM_OPTIONS 52 #define IDM_HELP 0 #define IDM_DIR 1 #define IDM_CONNECT 2 #define IDM_SEND 3 #define IDM_REC 4 #define IDM_QUIT 5 #define IDM_COMPORT 6 #define IDM_BAUDRATE 7 #define IDM_DATABITS 8 #define IDM_STOPBITS 9 #define IDM_PARITY 10 #define IDM_ABOUT 11 #define IDM_HELPMENU 12 #define IDM_TERMHELP 13 #define IDM_SENDFN 14 #define IDM_DIRPATH 15 #define ID_SENDFN 16 #define ID_DIRPATH 17 #define IDM_DIREND 18 #define IDM_COLORS 19 #define IDM_WHITE 20 #define IDM_GREEN 21 #define IDM_AMBER 22 #define IDM_C1 23 #define IDM_C2 24 #define ID_OK 25 #define ID_COM1 100 #define ID_COM2 101 #define ID_B110 120 #define ID_B150 121 #define ID_B300 122 #define ID_B600 123 #define ID_B1200 124 #define ID_B2400 125 #define ID_B4800 126 #define ID_B9600 127 #define ID_B19K2 128 #define ID_DATA7 137 #define ID_DATA8 138 #define ID_STOP1 141 #define ID_STOP2 142 #define ID_EVEN 150 #define ID_ODD 151 #define ID_NONE 152 [LISTING SEVENTEEN] IMPLEMENTATION MODULE DataLink; (* Sends and Receives Packets for PCKermit *) FROM ElapsedTime IMPORT StartTime, GetTime; FROM Screen IMPORT ClrScr, WriteString, WriteLn; FROM OS2DEF IMPORT HIWORD, LOWORD; FROM PMWIN IMPORT MPARAM, MPFROM2SHORT, WinPostMsg; FROM Shell IMPORT ChildFrameWindow, comport; FROM CommPort IMPORT CommStatus, GetChar, SendChar; FROM PAD IMPORT PacketType, yourNPAD, yourPADC, yourEOL; FROM KH IMPORT COM_OFF; FROM SYSTEM IMPORT BYTE; IMPORT ASCII; CONST MAXtime = 100; (* hundredths of a second -- i.e., one second *) MAXsohtrys = 100; DL_BadCS = 1; DL_NoSOH = 2; TYPE SMALLSET = SET OF [0..7]; (* BYTE *) VAR ch : CHAR; status : CommStatus; PROCEDURE Delay (t : CARDINAL); (* delay time in milliseconds *) VAR tmp : LONGINT; BEGIN tmp := t DIV 10; StartTime; WHILE GetTime() < tmp DO END; END Delay; PROCEDURE ByteAnd (a, b : BYTE) : BYTE; BEGIN RETURN BYTE (SMALLSET (a) * SMALLSET (b)); END ByteAnd; PROCEDURE Char (c : INTEGER) : CHAR; (* converts a number 0-95 into a printable character *) BEGIN RETURN (CHR (CARDINAL (ABS (c) + 32))); END Char; PROCEDURE UnChar (c : CHAR) : INTEGER; (* converts a character into its corresponding number *) BEGIN RETURN (ABS (INTEGER (ORD (c)) - 32)); END UnChar; PROCEDURE FlushUART; (* ensure no characters left in UART holding registers *) BEGIN Delay (500); REPEAT status := GetChar (comport - COM_OFF, ch); UNTIL status = NoCharacter; END FlushUART; PROCEDURE SendPacket (s : PacketType); (* Adds SOH and CheckSum to packet *) VAR i : CARDINAL; checksum : INTEGER; BEGIN Delay (10); (* give host a chance to catch its breath *) FOR i := 1 TO yourNPAD DO status := SendChar (comport - COM_OFF, yourPADC, FALSE); END; status := SendChar (comport - COM_OFF, ASCII.soh, FALSE); i := 1; checksum := 0; WHILE s[i] # 0C DO INC (checksum, ORD (s[i])); status := SendChar (comport - COM_OFF, s[i], FALSE); INC (i); END; checksum := checksum + (INTEGER (BITSET (checksum) * {7, 6}) DIV 64); checksum := INTEGER (BITSET (checksum) * {5, 4, 3, 2, 1, 0}); status := SendChar (comport - COM_OFF, Char (checksum), FALSE); IF yourEOL # 0C THEN status := SendChar (comport - COM_OFF, yourEOL, FALSE); END; END SendPacket; PROCEDURE ReceivePacket (VAR r : PacketType) : BOOLEAN; (* strips SOH and checksum -- returns status: TRUE = good packet *) (* received; FALSE = timed out waiting for packet or checksum error *) VAR sohtrys : INTEGER; i, len : INTEGER; ch : CHAR; checksum : INTEGER; mycheck, yourcheck : CHAR; BEGIN sohtrys := MAXsohtrys; REPEAT StartTime; REPEAT status := GetChar (comport - COM_OFF, ch); UNTIL (status = Success) OR (GetTime() > MAXtime); ch := CHAR (ByteAnd (ch, 177C)); (* mask off MSB *) (* skip over up to MAXsohtrys padding characters, *) (* but allow only MAXsohtrys/10 timeouts *) IF status = Success THEN DEC (sohtrys); ELSE DEC (sohtrys, 10); END; UNTIL (ch = ASCII.soh) OR (sohtrys <= 0); IF ch = ASCII.soh THEN (* receive rest of packet *) StartTime; REPEAT status := GetChar (comport - COM_OFF, ch); UNTIL (status = Success) OR (GetTime() > MAXtime); ch := CHAR (ByteAnd (ch, 177C)); len := UnChar (ch); r[1] := ch; checksum := ORD (ch); i := 2; (* on to second character in packet -- after LEN *) REPEAT StartTime; REPEAT status := GetChar (comport - COM_OFF, ch); UNTIL (status = Success) OR (GetTime() > MAXtime); ch := CHAR (ByteAnd (ch, 177C)); r[i] := ch; INC (i); INC (checksum, (ORD (ch))); UNTIL (i > len); (* get checksum character *) StartTime; REPEAT status := GetChar (comport - COM_OFF, ch); UNTIL (status = Success) OR (GetTime() > MAXtime); ch := CHAR (ByteAnd (ch, 177C)); yourcheck := ch; r[i] := 0C; checksum := checksum + (INTEGER (BITSET (checksum) * {7, 6}) DIV 64); checksum := INTEGER (BITSET (checksum) * {5, 4, 3, 2, 1, 0}); mycheck := Char (checksum); IF mycheck = yourcheck THEN (* checksum OK *) RETURN TRUE; ELSE (* ERROR!!! *) WinPostMsg (ChildFrameWindow, WM_DL, MPFROM2SHORT (DL_BadCS, 0), 0); RETURN FALSE; END; ELSE WinPostMsg (ChildFrameWindow, WM_DL, MPFROM2SHORT (DL_NoSOH, 0), 0); RETURN FALSE; END; END ReceivePacket; PROCEDURE DoDLMsg (mp1, mp2 : MPARAM); (* Process DataLink Messages *) BEGIN CASE LOWORD (mp1) OF DL_BadCS: WriteString ("Bad Checksum"); WriteLn; | DL_NoSOH: WriteString ("No SOH"); WriteLn; ELSE (* Do Nothing *) END; END DoDLMsg; END DataLink. [LISTING EIGHTEEN] #include #include "pckermit.h" ICON IDM_KERMIT pckermit.ico MENU IDM_KERMIT BEGIN SUBMENU "~File", IDM_FILE BEGIN MENUITEM "~Directory...", IDM_DIR MENUITEM "~Connect\t^C", IDM_CONNECT MENUITEM "~Send...\t^S", IDM_SEND MENUITEM "~Receive...\t^R", IDM_REC MENUITEM SEPARATOR MENUITEM "E~xit\t^X", IDM_QUIT MENUITEM "A~bout PCKermit...", IDM_ABOUT END SUBMENU "~Options", IDM_OPTIONS BEGIN MENUITEM "~COM port...", IDM_COMPORT MENUITEM "~Baud rate...", IDM_BAUDRATE MENUITEM "~Data bits...", IDM_DATABITS MENUITEM "~Stop bits...", IDM_STOPBITS MENUITEM "~Parity bits...", IDM_PARITY END SUBMENU "~Colors", IDM_COLORS BEGIN MENUITEM "~White Mono", IDM_WHITE MENUITEM "~Green Mono", IDM_GREEN MENUITEM "~Amber Mono", IDM_AMBER MENUITEM "Full Color ~1", IDM_C1 MENUITEM "Full Color ~2", IDM_C2 END MENUITEM "F1=Help", IDM_HELP, MIS_HELP | MIS_BUTTONSEPARATOR END ACCELTABLE IDM_KERMIT BEGIN "^C", IDM_CONNECT "^S", IDM_SEND "^R", IDM_REC "^X", IDM_QUIT END DLGTEMPLATE IDM_COMPORT LOADONCALL MOVEABLE DISCARDABLE BEGIN DIALOG "", IDM_COMPORT, 129, 91, 143, 54, FS_NOBYTEALIGN | FS_DLGBORDER | WS_VISIBLE | WS_CLIPSIBLINGS | WS_SAVEBITS BEGIN CONTROL "Select COM Port", IDM_COMPORT, 10, 9, 83, 38, WC_STATIC, SS_GROUPBOX | WS_VISIBLE CONTROL "COM1", ID_COM1, 30, 25, 43, 10, WC_BUTTON, BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE CONTROL "COM2", ID_COM2, 30, 15, 39, 10, WC_BUTTON, BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE CONTROL "OK", ID_OK, 101, 10, 38, 12, WC_BUTTON, BS_PUSHBUTTON | BS_DEFAULT | WS_GROUP | WS_TABSTOP | WS_VISIBLE END END DLGTEMPLATE IDM_BAUDRATE LOADONCALL MOVEABLE DISCARDABLE BEGIN DIALOG "", IDM_BAUDRATE, 131, 54, 142, 115, FS_NOBYTEALIGN | FS_DLGBORDER | WS_VISIBLE | WS_CLIPSIBLINGS | WS_SAVEBITS BEGIN CONTROL "Select Baud Rate", IDM_BAUDRATE, 8, 6, 85, 107, WC_STATIC, SS_GROUPBOX | WS_VISIBLE CONTROL "110 Baud", ID_B110, 20, 90, 62, 10, WC_BUTTON, BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE CONTROL "150 Baud", ID_B150, 20, 80, 57, 10, WC_BUTTON, BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE CONTROL "300 Baud", ID_B300, 20, 70, 58, 10, WC_BUTTON, BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE CONTROL "600 Baud", ID_B600, 20, 60, 54, 10, WC_BUTTON, BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE CONTROL "1200 Baud", ID_B1200, 20, 50, 59, 10, WC_BUTTON, BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE CONTROL "2400 Baud", ID_B2400, 20, 40, 63, 10, WC_BUTTON, BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE CONTROL "4800 Baud", ID_B4800, 20, 30, 62, 10, WC_BUTTON, BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE CONTROL "9600 Baud", ID_B9600, 20, 20, 59, 10, WC_BUTTON, BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE CONTROL "19,200 Baud", ID_B19K2, 20, 10, 69, 10, WC_BUTTON, BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE CONTROL "OK", ID_OK, 100, 8, 38, 12, WC_BUTTON, BS_PUSHBUTTON | BS_DEFAULT | WS_GROUP | WS_TABSTOP | WS_VISIBLE END END DLGTEMPLATE IDM_DATABITS LOADONCALL MOVEABLE DISCARDABLE BEGIN DIALOG "", IDM_DATABITS, 137, 80, 140, 56, FS_NOBYTEALIGN | FS_DLGBORDER | WS_VISIBLE | WS_SAVEBITS BEGIN CONTROL "Select Data Bits", IDM_DATABITS, 8, 11, 80, 36, WC_STATIC, SS_GROUPBOX | WS_VISIBLE CONTROL "7 Data Bits", ID_DATA7, 15, 25, 67, 10, WC_BUTTON, BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE CONTROL "8 Data Bits", ID_DATA8, 15, 15, 64, 10, WC_BUTTON, BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE CONTROL "OK", ID_OK, 96, 12, 38, 12, WC_BUTTON, BS_PUSHBUTTON | BS_DEFAULT | WS_GROUP | WS_TABSTOP | WS_VISIBLE END END DLGTEMPLATE IDM_STOPBITS LOADONCALL MOVEABLE DISCARDABLE BEGIN DIALOG "", IDM_STOPBITS, 139, 92, 140, 43, FS_NOBYTEALIGN | FS_DLGBORDER | WS_VISIBLE | WS_SAVEBITS BEGIN CONTROL "Select Stop Bits", IDM_STOPBITS, 9, 6, 80, 32, WC_STATIC, SS_GROUPBOX | WS_VISIBLE CONTROL "1 Stop Bit", ID_STOP1, 20, 20, 57, 10, WC_BUTTON, BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE CONTROL "2 Stop Bits", ID_STOP2, 20, 10, 60, 10, WC_BUTTON, BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE CONTROL "OK", ID_OK, 96, 8, 38, 12, WC_BUTTON, BS_PUSHBUTTON | BS_DEFAULT | WS_GROUP | WS_TABSTOP | WS_VISIBLE END END DLGTEMPLATE IDM_PARITY LOADONCALL MOVEABLE DISCARDABLE BEGIN DIALOG "", IDM_PARITY, 138, 84, 134, 57, FS_NOBYTEALIGN | FS_DLGBORDER | WS_VISIBLE | WS_SAVEBITS BEGIN CONTROL "Select Parity", IDM_PARITY, 12, 6, 64, 46, WC_STATIC, SS_GROUPBOX | WS_VISIBLE CONTROL "Even", ID_EVEN, 25, 30, 40, 10, WC_BUTTON, BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE CONTROL "Odd", ID_ODD, 25, 20, 38, 10, WC_BUTTON, BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE CONTROL "None", ID_NONE, 25, 10, 40, 10, WC_BUTTON, BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE CONTROL "OK", ID_OK, 88, 8, 38, 12, WC_BUTTON, BS_PUSHBUTTON | BS_DEFAULT | WS_GROUP | WS_TABSTOP | WS_VISIBLE END END DLGTEMPLATE IDM_ABOUT LOADONCALL MOVEABLE DISCARDABLE BEGIN DIALOG "", IDM_ABOUT, 93, 74, 229, 88, FS_NOBYTEALIGN | FS_DLGBORDER | WS_VISIBLE | WS_SAVEBITS BEGIN ICON IDM_KERMIT -1, 12, 64, 22, 16 CONTROL "PCKermit for OS/2", 256, 67, 70, 82, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "Copyright (c) 1990 by Brian R. Anderson", 257, 27, 30, 172, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "Microcomputer to Mainframe Communications", 259, 13, 50, 199, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL " OK ", 258, 88, 10, 38, 12, WC_BUTTON, BS_PUSHBUTTON | BS_DEFAULT | WS_TABSTOP | WS_VISIBLE END END DLGTEMPLATE IDM_HELPMENU LOADONCALL MOVEABLE DISCARDABLE BEGIN DIALOG "", IDM_HELPMENU, 83, 45, 224, 125, FS_NOBYTEALIGN | FS_DLGBORDER | WS_VISIBLE | WS_CLIPSIBLINGS | WS_SAVEBITS BEGIN ICON IDM_KERMIT -1, 14, 99, 21, 16 CONTROL "PCKermit Help Menu", 256, 64, 106, 91, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "set communications Options .................. Alt, O", 258, 10, 80, 201, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "Connect to Host ................................... Alt, F; C", 259, 10, 70, 204, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "Directory .............................................. Alt, F; D", 260, 10, 60, 207, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "Send a File .......................................... Alt, F; S", 261, 10, 50, 207, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "Receive a File ...................................... Alt, F; R", 262, 10, 40, 209, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "Exit ...................................................... Alt, F; X", 263, 10, 30, 205, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "OK", 264, 83, 9, 38, 12, WC_BUTTON, BS_PUSHBUTTON | WS_TABSTOP | WS_VISIBLE | BS_DEFAULT END END DLGTEMPLATE IDM_TERMHELP LOADONCALL MOVEABLE DISCARDABLE BEGIN DIALOG "", IDM_TERMHELP, 81, 20, 238, 177, FS_NOBYTEALIGN | FS_DLGBORDER | WS_VISIBLE | WS_CLIPSIBLINGS | WS_SAVEBITS BEGIN CONTROL "^E = Echo mode", 256, 10, 160, 72, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "^L = Local echo mode", 257, 10, 150, 97, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "^T = Terminal Mode (no echo)", 258, 10, 140, 131, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "^N = Newline mode ( --> )", 259, 10, 130, 165, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "^O = Newline mode OFF", 260, 10, 120, 109, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "Televideo TVI950 / IBM 7171 Terminal Emulation", 261, 10, 100, 217, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "Sh-F1 - Sh-F12 = PF1 - PF12", 262, 10, 90, 135, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "Home = Clear", 263, 10, 80, 119, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "PgDn = Page Down (as used in PROFS)", 264, 10, 70, 228, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "PgUp = Page Up (as used in PROFS)", 265, 10, 60, 227, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "Insert = Insert (Enter to Clear)", 266, 10, 40, 221, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "Delete = Delete", 267, 10, 30, 199, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "Control-G = Reset (rewrites the screen)", 268, 10, 20, 222, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "Cursor Keys (i.e., Up, Down, Left, Right) all work.", 269, 10, 10, 229, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "OK", 270, 193, 158, 38, 12, WC_BUTTON, BS_PUSHBUTTON | BS_DEFAULT | WS_TABSTOP | WS_VISIBLE CONTROL "End = End (as used in PROFS)", 271, 10, 50, 209, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE END END DLGTEMPLATE IDM_SENDFN LOADONCALL MOVEABLE DISCARDABLE BEGIN DIALOG "", IDM_SENDFN, 113, 90, 202, 60, FS_NOBYTEALIGN | FS_DLGBORDER | WS_VISIBLE | WS_SAVEBITS BEGIN CONTROL "Send File", 256, 4, 4, 195, 24, WC_STATIC, SS_GROUPBOX | WS_GROUP | WS_VISIBLE CONTROL "Enter filename:", 257, 13, 11, 69, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE ICON IDM_KERMIT -1, 15, 38, 22, 16 CONTROL "PCKermit for OS/2", 259, 59, 45, 82, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "OK", 260, 154, 36, 38, 12, WC_BUTTON, BS_PUSHBUTTON | WS_TABSTOP | WS_VISIBLE | BS_DEFAULT CONTROL "", ID_SENDFN, 89, 10, 98, 8, WC_ENTRYFIELD, ES_LEFT | ES_MARGIN | WS_TABSTOP | WS_VISIBLE END END DLGTEMPLATE IDM_DIRPATH LOADONCALL MOVEABLE DISCARDABLE BEGIN DIALOG "", IDM_DIRPATH, 83, 95, 242, 46, FS_NOBYTEALIGN | FS_DLGBORDER | WS_VISIBLE | WS_SAVEBITS BEGIN CONTROL "Directory", 256, 7, 5, 227, 24, WC_STATIC, SS_GROUPBOX | WS_GROUP | WS_VISIBLE CONTROL "Path:", 257, 28, 11, 26, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE CONTROL "OK", 258, 185, 31, 38, 12, WC_BUTTON, BS_PUSHBUTTON | WS_TABSTOP | WS_VISIBLE | BS_DEFAULT CONTROL "*.*", ID_DIRPATH, 57, 11, 166, 8, WC_ENTRYFIELD, ES_LEFT | ES_AUTOSCROLL | ES_MARGIN | WS_TABSTOP | WS_VISIBLE END END DLGTEMPLATE IDM_DIREND LOADONCALL MOVEABLE DISCARDABLE BEGIN DIALOG "", IDM_DIREND, 149, 18, 101, 27, FS_NOBYTEALIGN | FS_DLGBORDER | WS_VISIBLE | WS_SAVEBITS BEGIN CONTROL "Cancel", 256, 30, 2, 38, 12, WC_BUTTON, BS_PUSHBUTTON | BS_DEFAULT | WS_TABSTOP | WS_VISIBLE CONTROL "Directory Complete", 257, 9, 16, 84, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE END END [LISTING NINETEEN] HEAPSIZE 16384 STACKSIZE 16384 EXPORTS WindowProc ChildWindowProc [FILE PCKERMIT] OS2DEF.SYM: OS2DEF.DEF M2 OS2DEF.DEF/OUT:OS2DEF.SYM OS2DEF.OBJ: OS2DEF.MOD OS2DEF.SYM M2 OS2DEF.MOD/OUT:OS2DEF.OBJ PMWIN.SYM: PMWIN.DEF OS2DEF.SYM M2 PMWIN.DEF/OUT:PMWIN.SYM PMWIN.OBJ: PMWIN.MOD OS2DEF.SYM PMWIN.SYM M2 PMWIN.MOD/OUT:PMWIN.OBJ KH.SYM: KH.DEF M2 KH.DEF/OUT:KH.SYM KH.OBJ: KH.MOD KH.SYM M2 KH.MOD/OUT:KH.OBJ SHELL.SYM: SHELL.DEF PMWIN.SYM OS2DEF.SYM M2 SHELL.DEF/OUT:SHELL.SYM TERM.SYM: TERM.DEF M2 TERM.DEF/OUT:TERM.SYM PAD.SYM: PAD.DEF PMWIN.SYM M2 PAD.DEF/OUT:PAD.SYM DATALINK.SYM: DATALINK.DEF PAD.SYM PMWIN.SYM M2 DATALINK.DEF/OUT:DATALINK.SYM PMAVIO.SYM: PMAVIO.DEF PMWIN.SYM OS2DEF.SYM M2 PMAVIO.DEF/OUT:PMAVIO.SYM PMAVIO.OBJ: PMAVIO.MOD PMAVIO.SYM M2 PMAVIO.MOD/OUT:PMAVIO.OBJ PMGPI.SYM: PMGPI.DEF OS2DEF.SYM M2 PMGPI.DEF/OUT:PMGPI.SYM PMGPI.OBJ: PMGPI.MOD OS2DEF.SYM PMGPI.SYM M2 PMGPI.MOD/OUT:PMGPI.OBJ COMMPORT.SYM: COMMPORT.DEF M2 COMMPORT.DEF/OUT:COMMPORT.SYM COMMPORT.OBJ: COMMPORT.MOD COMMPORT.SYM M2 COMMPORT.MOD/OUT:COMMPORT.OBJ FILES.SYM: FILES.DEF M2 FILES.DEF/OUT:FILES.SYM PCKERMIT.OBJ: PCKERMIT.MOD SHELL.SYM KH.SYM PMWIN.SYM OS2DEF.SYM M2 PCKERMIT.MOD/OUT:PCKERMIT.OBJ SCREEN.SYM: SCREEN.DEF PMAVIO.SYM M2 SCREEN.DEF/OUT:SCREEN.SYM SCREEN.OBJ: SCREEN.MOD SCREEN.SYM M2 SCREEN.MOD/OUT:SCREEN.OBJ FILES.OBJ: FILES.MOD FILES.SYM M2 FILES.MOD/OUT:FILES.OBJ SHELL.OBJ: SHELL.MOD COMMPORT.SYM KH.SYM PMGPI.SYM PMWIN.SYM PMAVIO.SYM - SCREEN.SYM DATALINK.SYM PAD.SYM TERM.SYM OS2DEF.SYM SHELL.SYM M2 SHELL.MOD/OUT:SHELL.OBJ TERM.OBJ: TERM.MOD COMMPORT.SYM KH.SYM SHELL.SYM PMWIN.SYM SCREEN.SYM TERM.SYM M2 TERM.MOD/OUT:TERM.OBJ PAD.OBJ: PAD.MOD DATALINK.SYM KH.SYM SHELL.SYM PMWIN.SYM COMMPORT.SYM - FILES.SYM OS2DEF.SYM SCREEN.SYM PAD.SYM M2 PAD.MOD/OUT:PAD.OBJ DATALINK.OBJ: DATALINK.MOD KH.SYM PAD.SYM COMMPORT.SYM SHELL.SYM PMWIN.SYM - OS2DEF.SYM SCREEN.SYM DATALINK.SYM M2 DATALINK.MOD/OUT:DATALINK.OBJ PCKERMIT.res: PCKERMIT.rc PCKERMIT.h PCKERMIT.ico rc -r PCKERMIT.rc PCKERMIT.EXE: OS2DEF.OBJ PMWIN.OBJ KH.OBJ PMAVIO.OBJ PMGPI.OBJ COMMPORT.OBJ - PCKERMIT.OBJ SCREEN.OBJ FILES.OBJ SHELL.OBJ TERM.OBJ PAD.OBJ DATALINK.OBJ LINK @PCKERMIT.LNK rc PCKERMIT.res PCKERMIT.exe: PCKERMIT.res rc PCKERMIT.res [FILE PCKERMIT.LNK] KH.OBJ+ pckermit.OBJ+ SCREEN.OBJ+ COMMPORT.OBJ+ FILES.OBJ+ SHELL.OBJ+ TERM.OBJ+ PAD.OBJ+ DATALINK.OBJ pckermit pckermit PM+ M2LIB+ DOSCALLS+ OS2 pckermit.edf [FILE PAD.MOD] IMPLEMENTATION MODULE PAD; (* Packet Assembler/Disassembler for Kermit *) FROM SYSTEM IMPORT ADR; FROM Storage IMPORT ALLOCATE, DEALLOCATE; FROM Screen IMPORT ClrScr, WriteString, WriteInt, WriteHex, WriteLn; FROM OS2DEF IMPORT HIWORD, LOWORD; FROM DosCalls IMPORT ExitType, DosExit; FROM Strings IMPORT Length, Assign; FROM FileSystem IMPORT File; FROM Directories IMPORT FileAttributes, AttributeSet, DirectoryEntry, FindFirst, FindNext; FROM Files IMPORT Status, FileType, Open, Create, CloseFile, Get, Put, DoWrite; FROM PMWIN IMPORT MPARAM, MPFROM2SHORT, WinPostMsg; FROM Shell IMPORT ChildFrameWindow, comport; FROM KH IMPORT COM_OFF; FROM DataLink IMPORT FlushUART, SendPacket, ReceivePacket; FROM SYSTEM IMPORT BYTE; IMPORT ASCII; CONST myMAXL = 94; myTIME = 10; myNPAD = 0; myPADC = 0C; myEOL = 0C; myQCTL = '#'; myQBIN = '&'; myCHKT = '1'; (* one character checksum *) MAXtrys = 5; (* From DEFINITION MODULE: PAD_Quit = 0; *) PAD_SendPacket = 1; PAD_ResendPacket = 2; PAD_NoSuchFile = 3; PAD_ExcessiveErrors = 4; PAD_ProbClSrcFile = 5; PAD_ReceivedPacket = 6; PAD_Filename = 7; PAD_RequestRepeat = 8; PAD_DuplicatePacket = 9; PAD_UnableToOpen = 10; PAD_ProbClDestFile = 11; PAD_ErrWrtFile = 12; PAD_Msg = 13; TYPE (* From Definition Module: PacketType = ARRAY [1..100] OF CHAR; *) SMALLSET = SET OF [0..7]; (* a byte *) VAR yourMAXL : INTEGER; (* maximum packet length -- up to 94 *) yourTIME : INTEGER; (* time out -- seconds *) (* From Definition Module yourNPAD : INTEGER; (* number of padding characters *) yourPADC : CHAR; (* padding characters *) yourEOL : CHAR; (* End Of Line -- terminator *) *) yourQCTL : CHAR; (* character for quoting controls '#' *) yourQBIN : CHAR; (* character for quoting binary '&' *) yourCHKT : CHAR; (* check type -- 1 = checksum, etc. *) sF, rF : File; (* files being sent/received *) InputFileOpen : BOOLEAN; rFname : ARRAY [0..20] OF CHAR; sP, rP : PacketType; (* packets sent/received *) sSeq, rSeq : INTEGER; (* sequence numbers *) PktNbr : INTEGER; (* actual packet number -- no repeats up to 32,000 *) ErrorMsg : ARRAY [0..40] OF CHAR; PROCEDURE PtrToStr (mp : MPARAM; VAR s : ARRAY OF CHAR); (* Convert a pointer to a string into a string *) TYPE PC = POINTER TO CHAR; VAR p : PC; i : CARDINAL; c : CHAR; BEGIN i := 0; REPEAT p := PC (mp); c := p^; s[i] := c; INC (i); INC (mp); UNTIL c = 0C; END PtrToStr; PROCEDURE DoPADMsg (mp1, mp2 : MPARAM); (* Output messages for Packet Assembler/Disassembler *) VAR Message : ARRAY [0..40] OF CHAR; BEGIN CASE LOWORD (mp1) OF PAD_SendPacket: WriteString ("Sent Packet #"); WriteInt (LOWORD (mp2), 5); WriteString (" (ID: "); WriteHex (HIWORD (mp2), 2); WriteString ("h)"); | PAD_ResendPacket: WriteString ("ERROR -- Resending:"); WriteLn; WriteString (" Packet #"); WriteInt (LOWORD (mp2), 5); WriteString (" (ID: "); WriteHex (HIWORD (mp2), 2); WriteString ("h)"); | PAD_NoSuchFile: WriteString ("No such file: "); PtrToStr (mp2, Message); WriteString (Message); | PAD_ExcessiveErrors: WriteString ("Excessive errors ..."); | PAD_ProbClSrcFile: WriteString ("Problem closing source file..."); | PAD_ReceivedPacket: WriteString ("Received Packet #"); WriteInt (LOWORD (mp2), 5); WriteString (" (ID: "); WriteHex (HIWORD (mp2), 2); WriteString ("h)"); | PAD_Filename: WriteString ("Filename = "); PtrToStr (mp2, Message); WriteString (Message); | PAD_RequestRepeat: WriteString ("ERROR -- Requesting Repeat:"); WriteLn; WriteString (" Packet #"); WriteInt (LOWORD (mp2), 5); WriteString (" (ID: "); WriteHex (HIWORD (mp2), 2); WriteString ("h)"); | PAD_DuplicatePacket: WriteString ("Discarding Duplicate:"); WriteLn; WriteString (" Packet #"); WriteString (" (ID: "); WriteHex (HIWORD (mp2), 2); WriteString ("h)"); | PAD_UnableToOpen: WriteString ("Unable to open file: "); PtrToStr (mp2, Message); WriteString (Message); | PAD_ProbClDestFile: WriteString ("Error closing file: "); PtrToStr (mp2, Message); WriteString (Message); | PAD_ErrWrtFile: WriteString ("Error writing to file: "); PtrToStr (mp2, Message); WriteString (Message); | PAD_Msg: PtrToStr (mp2, Message); WriteString (Message); ELSE (* Do Nothing *) END; WriteLn; END DoPADMsg; PROCEDURE CloseInput; (* Close the input file, if it exists. Reset Input File Open flag *) BEGIN IF InputFileOpen THEN IF CloseFile (sF, Input) = Done THEN InputFileOpen := FALSE; ELSE WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_ProbClSrcFile, 0), ADR (sFname)); END; END; END CloseInput; PROCEDURE NormalQuit; (* Exit from Thread, Post message to Window *) BEGIN WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_Quit, 0), 0); DosExit (EXIT_THREAD, 0); END NormalQuit; PROCEDURE ErrorQuit; (* Exit from Thread, Post message to Window *) BEGIN WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_Error, 0), 0); DosExit (EXIT_THREAD, 0); END ErrorQuit; PROCEDURE ByteXor (a, b : BYTE) : BYTE; BEGIN RETURN BYTE (SMALLSET (a) / SMALLSET (b)); END ByteXor; PROCEDURE Char (c : INTEGER) : CHAR; (* converts a number 0-94 into a printable character *) BEGIN RETURN (CHR (CARDINAL (ABS (c) + 32))); END Char; PROCEDURE UnChar (c : CHAR) : INTEGER; (* converts a character into its corresponding number *) BEGIN RETURN (ABS (INTEGER (ORD (c)) - 32)); END UnChar; PROCEDURE TellError (Seq : INTEGER); (* Send error packet *) BEGIN sP[1] := Char (15); sP[2] := Char (Seq); sP[3] := 'E'; (* E-type packet *) sP[4] := 'R'; (* error message starts *) sP[5] := 'e'; sP[6] := 'm'; sP[7] := 'o'; sP[8] := 't'; sP[9] := 'e'; sP[10] := ' '; sP[11] := 'A'; sP[12] := 'b'; sP[13] := 'o'; sP[14] := 'r'; sP[15] := 't'; sP[16] := 0C; SendPacket (sP); END TellError; PROCEDURE ShowError (p : PacketType); (* Output contents of error packet to the screen *) VAR i : INTEGER; BEGIN FOR i := 4 TO UnChar (p[1]) DO ErrorMsg[i - 4] := p[i]; END; ErrorMsg[i - 4] := 0C; WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_Msg, 0), ADR (ErrorMsg)); END ShowError; PROCEDURE youInit (type : CHAR); (* I initialization YOU for Send and Receive *) BEGIN sP[1] := Char (11); (* Length *) sP[2] := Char (0); (* Sequence *) sP[3] := type; sP[4] := Char (myMAXL); sP[5] := Char (myTIME); sP[6] := Char (myNPAD); sP[7] := CHAR (ByteXor (myPADC, 100C)); sP[8] := Char (ORD (myEOL)); sP[9] := myQCTL; sP[10] := myQBIN; sP[11] := myCHKT; sP[12] := 0C; (* terminator *) SendPacket (sP); END youInit; PROCEDURE myInit; (* YOU initialize ME for Send and Receive *) VAR len : INTEGER; BEGIN len := UnChar (rP[1]); IF len >= 4 THEN yourMAXL := UnChar (rP[4]); ELSE yourMAXL := 94; END; IF len >= 5 THEN yourTIME := UnChar (rP[5]); ELSE yourTIME := 10; END; IF len >= 6 THEN yourNPAD := UnChar (rP[6]); ELSE yourNPAD := 0; END; IF len >= 7 THEN yourPADC := CHAR (ByteXor (rP[7], 100C)); ELSE yourPADC := 0C; END; IF len >= 8 THEN yourEOL := CHR (UnChar (rP[8])); ELSE yourEOL := 0C; END; IF len >= 9 THEN yourQCTL := rP[9]; ELSE yourQCTL := 0C; END; IF len >= 10 THEN yourQBIN := rP[10]; ELSE yourQBIN := 0C; END; IF len >= 11 THEN yourCHKT := rP[11]; IF yourCHKT # myCHKT THEN yourCHKT := '1'; END; ELSE yourCHKT := '1'; END; END myInit; PROCEDURE SendInit; BEGIN youInit ('S'); END SendInit; PROCEDURE SendFileName; VAR i, j : INTEGER; BEGIN (* send file name *) i := 4; j := 0; WHILE sFname[j] # 0C DO sP[i] := sFname[j]; INC (i); INC (j); END; sP[1] := Char (j + 3); sP[2] := Char (sSeq); sP[3] := 'F'; (* filename packet *) sP[i] := 0C; SendPacket (sP); END SendFileName; PROCEDURE SendEOF; BEGIN sP[1] := Char (3); sP[2] := Char (sSeq); sP[3] := 'Z'; (* end of file *) sP[4] := 0C; SendPacket (sP); END SendEOF; PROCEDURE SendEOT; BEGIN sP[1] := Char (3); sP[2] := Char (sSeq); sP[3] := 'B'; (* break -- end of transmit *) sP[4] := 0C; SendPacket (sP); END SendEOT; PROCEDURE GetAck() : BOOLEAN; (* Look for acknowledgement -- retry on timeouts or NAKs *) VAR Type : CHAR; Seq : INTEGER; retrys : INTEGER; AckOK : BOOLEAN; BEGIN WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_SendPacket, 0), MPFROM2SHORT (PktNbr, sSeq)); retrys := MAXtrys; LOOP IF Aborted THEN TellError (sSeq); CloseInput; ErrorQuit; END; IF ReceivePacket (rP) THEN Seq := UnChar (rP[2]); Type := rP[3]; IF (Seq = sSeq) AND (Type = 'Y') THEN AckOK := TRUE; ELSIF (Seq = (sSeq + 1) MOD 64) AND (Type = 'N') THEN AckOK := TRUE; (* NAK for (n + 1) taken as ACK for n *) ELSIF Type = 'E' THEN ShowError (rP); AckOK := FALSE; retrys := 0; ELSE AckOK := FALSE; END; ELSE AckOK := FALSE; END; IF AckOK OR (retrys = 0) THEN EXIT; ELSE WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_ResendPacket, 0), MPFROM2SHORT (PktNbr, sSeq)); DEC (retrys); FlushUART; SendPacket (sP); END; END; IF AckOK THEN INC (PktNbr); sSeq := (sSeq + 1) MOD 64; RETURN TRUE; ELSE RETURN FALSE; END; END GetAck; PROCEDURE GetInitAck() : BOOLEAN; (* configuration for remote station *) BEGIN IF GetAck() THEN myInit; RETURN TRUE; ELSE RETURN FALSE; END; END GetInitAck; PROCEDURE Send; (* Send one or more files: sFname may be ambiguous *) TYPE LP = POINTER TO LIST; (* list of filenames *) LIST = RECORD fn : ARRAY [0..20] OF CHAR; next : LP; END; VAR gotFN : BOOLEAN; attr : AttributeSet; ent : DirectoryEntry; front, back, t : LP; (* add at back of queue, remove from front *) BEGIN Aborted := FALSE; InputFileOpen := FALSE; front := NIL; back := NIL; attr := AttributeSet {}; (* normal files only *) IF Length (sFname) = 0 THEN WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_Msg, 0), ADR ("No file specified...")); ErrorQuit; ELSE gotFN := FindFirst (sFname, attr, ent); WHILE gotFN DO (* build up a list of file names *) ALLOCATE (t, SIZE (LIST)); Assign (ent.name, t^.fn); t^.next := NIL; IF front = NIL THEN front := t; (* start from empty queue *) ELSE back^.next := t; (* and to back of queue *) END; back := t; gotFN := FindNext (ent); END; END; IF front = NIL THEN WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_NoSuchFile, 0), ADR (sFname)); ErrorQuit; ELSE sSeq := 0; PktNbr := 0; FlushUART; SendInit; (* my configuration information *) IF NOT GetInitAck() THEN (* get your configuration information *) WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_ExcessiveErrors, 0), MPFROM2SHORT (0, 0)); ErrorQuit; END; WHILE front # NIL DO (* send the files *) Assign (front^.fn, sFname); PktNbr := 1; Send1; t := front; front := front^.next; DEALLOCATE (t, SIZE (LIST)); END; END; SendEOT; IF NOT GetAck() THEN WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_ExcessiveErrors, 0), MPFROM2SHORT (0, 0)); CloseInput; ErrorQuit; END; NormalQuit; END Send; PROCEDURE Send1; (* Send one file: sFname *) VAR ch : CHAR; i : INTEGER; BEGIN IF Open (sF, sFname) = Done THEN InputFileOpen := TRUE; ELSE; WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_NoSuchFile, 0), ADR (sFname)); ErrorQuit; END; WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_Filename, 0), ADR (sFname)); WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_Msg, 0), ADR ("( to abort file transfer.)")); SendFileName; IF NOT GetAck() THEN WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_ExcessiveErrors, 0), MPFROM2SHORT (0, 0)); CloseInput; ErrorQuit; END; (* send file *) i := 4; LOOP IF Get (sF, ch) = EOF THEN (* send current packet & terminate *) sP[1] := Char (i - 1); sP[2] := Char (sSeq); sP[3] := 'D'; (* data packet *) sP[i] := 0C; (* indicate end of packet *) SendPacket (sP); IF NOT GetAck() THEN WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_ExcessiveErrors, 0), MPFROM2SHORT (0, 0)); CloseInput; ErrorQuit; END; SendEOF; IF NOT GetAck() THEN WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_ExcessiveErrors, 0), MPFROM2SHORT (0, 0)); CloseInput; ErrorQuit; END; EXIT; END; IF i >= (yourMAXL - 4) THEN (* send current packet *) sP[1] := Char (i - 1); sP[2] := Char (sSeq); sP[3] := 'D'; sP[i] := 0C; SendPacket (sP); IF NOT GetAck() THEN WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_ExcessiveErrors, 0), MPFROM2SHORT (0, 0)); CloseInput; ErrorQuit; END; i := 4; END; (* add character to current packet -- update count *) IF ch > 177C THEN (* must be quoted (QBIN) and altered *) (* toggle bit 7 to turn it off *) ch := CHAR (ByteXor (ch, 200C)); sP[i] := myQBIN; INC (i); END; IF (ch < 40C) OR (ch = 177C) THEN (* quote (QCTL) and alter *) (* toggle bit 6 to turn it on *) ch := CHAR (ByteXor (ch, 100C)); sP[i] := myQCTL; INC (i); END; IF (ch = myQCTL) OR (ch = myQBIN) THEN (* must send it quoted *) sP[i] := myQCTL; INC (i); END; sP[i] := ch; INC (i); END; (* loop *) CloseInput; END Send1; PROCEDURE ReceiveInit() : BOOLEAN; (* receive my initialization information from you *) VAR RecOK : BOOLEAN; trys : INTEGER; BEGIN trys := 1; LOOP IF Aborted THEN TellError (rSeq); ErrorQuit; END; RecOK := ReceivePacket (rP) AND (rP[3] = 'S'); IF RecOK OR (trys = MAXtrys) THEN EXIT; ELSE INC (trys); SendNak; END; END; IF RecOK THEN myInit; RETURN TRUE; ELSE RETURN FALSE; END; END ReceiveInit; PROCEDURE SendInitAck; (* acknowledge your initialization of ME and send mine for YOU *) BEGIN WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_ReceivedPacket, 0), MPFROM2SHORT (PktNbr, rSeq)); INC (PktNbr); rSeq := (rSeq + 1) MOD 64; youInit ('Y'); END SendInitAck; PROCEDURE ValidFileChar (VAR ch : CHAR) : BOOLEAN; (* checks if character is one of 'A'..'Z', '0'..'9', makes upper case *) BEGIN ch := CAP (ch); RETURN ((ch >= 'A') AND (ch <= 'Z')) OR ((ch >= '0') AND (ch <= '9')); END ValidFileChar; TYPE HeaderType = (name, eot, fail); PROCEDURE ReceiveHeader() : HeaderType; (* receive the filename -- alter for local conditions, if necessary *) VAR i, j, k : INTEGER; RecOK : BOOLEAN; trys : INTEGER; BEGIN trys := 1; LOOP IF Aborted THEN TellError (rSeq); ErrorQuit; END; RecOK := ReceivePacket (rP) AND ((rP[3] = 'F') OR (rP[3] = 'B')); IF trys = MAXtrys THEN RETURN fail; ELSIF RecOK AND (rP[3] = 'F') THEN i := 4; (* data starts here *) j := 0; (* beginning of filename string *) WHILE (ValidFileChar (rP[i])) AND (j < 8) DO rFname[j] := rP[i]; INC (i); INC (j); END; REPEAT INC (i); UNTIL (ValidFileChar (rP[i])) OR (rP[i] = 0C); rFname[j] := '.'; INC (j); k := 0; WHILE (ValidFileChar (rP[i])) AND (k < 3) DO rFname[j + k] := rP[i]; INC (i); INC (k); END; rFname[j + k] := 0C; WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_Filename, 0), ADR (rFname)); RETURN name; ELSIF RecOK AND (rP[3] = 'B') THEN RETURN eot; ELSE INC (trys); SendNak; END; END; END ReceiveHeader; PROCEDURE SendNak; BEGIN WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_RequestRepeat, 0), MPFROM2SHORT (PktNbr, rSeq)); FlushUART; sP[1] := Char (3); (* LEN *) sP[2] := Char (rSeq); sP[3] := 'N'; (* negative acknowledgement *) sP[4] := 0C; SendPacket (sP); END SendNak; PROCEDURE SendAck (Seq : INTEGER); BEGIN IF Seq # rSeq THEN WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_DuplicatePacket, 0), MPFROM2SHORT (0, rSeq)); ELSE WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_ReceivedPacket, 0), MPFROM2SHORT (PktNbr, rSeq)); rSeq := (rSeq + 1) MOD 64; INC (PktNbr); END; sP[1] := Char (3); sP[2] := Char (Seq); sP[3] := 'Y'; (* acknowledgement *) sP[4] := 0C; SendPacket (sP); END SendAck; PROCEDURE Receive; (* Receives a file (or files) *) VAR ch, Type : CHAR; Seq : INTEGER; i : INTEGER; EOF, EOT, QBIN : BOOLEAN; trys : INTEGER; BEGIN Aborted := FALSE; WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_Msg, 0), ADR ("Ready to receive file(s)...")); WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_Msg, 0), ADR ("( to abort file transfer.)")); FlushUART; rSeq := 0; PktNbr := 0; IF NOT ReceiveInit() THEN (* your configuration information *) WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_ExcessiveErrors, 0), MPFROM2SHORT (0, 0)); ErrorQuit; END; SendInitAck; (* send my configuration information *) EOT := FALSE; WHILE NOT EOT DO CASE ReceiveHeader() OF eot : EOT := TRUE; EOF := TRUE; | name : IF Create (rF, rFname) # Done THEN WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_UnableToOpen, 0), ADR (rFname)); ErrorQuit; ELSE PktNbr := 1; EOF := FALSE; END; | fail : WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_ExcessiveErrors, 0), MPFROM2SHORT (0, 0)); ErrorQuit; END; SendAck (rSeq); (* acknowledge for name or eot *) trys := 1; (* initialize *) WHILE NOT EOF DO IF Aborted THEN TellError (rSeq); ErrorQuit; END; IF ReceivePacket (rP) THEN Seq := UnChar (rP[2]); Type := rP[3]; IF Type = 'Z' THEN EOF := TRUE; IF CloseFile (rF, Output) = Done THEN (* normal file termination *) ELSE WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_ProbClDestFile, 0), ADR (rFname)); ErrorQuit; END; trys := 1; (* good packet -- reset *) SendAck (rSeq); ELSIF Type = 'E' THEN ShowError (rP); ErrorQuit; ELSIF (Type = 'D') AND ((Seq + 1) MOD 64 = rSeq) THEN (* discard duplicate packet, and Ack anyway *) trys := 1; SendAck (Seq); ELSIF (Type = 'D') AND (Seq = rSeq) THEN (* put packet into file buffer *) i := 4; (* first data in packet *) WHILE rP[i] # 0C DO ch := rP[i]; INC (i); IF ch = yourQBIN THEN ch := rP[i]; INC (i); QBIN := TRUE; ELSE QBIN := FALSE; END; IF ch = yourQCTL THEN ch := rP[i]; INC (i); IF (ch # yourQCTL) AND (ch # yourQBIN) THEN ch := CHAR (ByteXor (ch, 100C)); END; END; IF QBIN THEN ch := CHAR (ByteXor (ch, 200C)); END; Put (ch); END; (* write file buffer to disk *) IF DoWrite (rF) # Done THEN WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_ErrWrtFile, 0), ADR (rFname)); ErrorQuit; END; trys := 1; SendAck (rSeq); ELSE INC (trys); IF trys = MAXtrys THEN WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_ExcessiveErrors, 0), MPFROM2SHORT (0, 0)); ErrorQuit; ELSE SendNak; END; END; ELSE INC (trys); IF trys = MAXtrys THEN WinPostMsg (ChildFrameWindow, WM_PAD, MPFROM2SHORT (PAD_ExcessiveErrors, 0), MPFROM2SHORT (0, 0)); ErrorQuit; ELSE SendNak; END; END; END; END; NormalQuit; END Receive; BEGIN (* module initialization *) yourEOL := ASCII.cr; yourNPAD := 0; yourPADC := 0C; END PAD.