Category : Files from Magazines
Archive   : DDJ0990.ZIP
Filename : ANDERSON.ASC

 
Output of file : ANDERSON.ASC contained in archive : DDJ0990.ZIP
_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.



  3 Responses to “Category : Files from Magazines
Archive   : DDJ0990.ZIP
Filename : ANDERSON.ASC

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/