/* Note, the following source files are concatenated together in this file: CMDS.P86 CONN.P86 GET.P86 KERMIT.P86 RECV.P86 SEND.P86 TRANS.P86 Each one begins with a comment containing its name, like the one below: */ /*---CMDS.P86---*/ $compact $optimize(3) cmds: do; declare true literally '0FFH'; declare false literally '00H'; $INCLUDE(:INC:LTKSEL.LIT) $INCLUDE(:INC:NEXCEP.LIT) $INCLUDE(:INC:IEXCEP.LIT) declare null literally '000H'; declare lf literally '0AH'; declare cr literally '0DH'; declare crlf literally 'cr,lf,null'; declare space literally '20H'; declare dollar literally '24H'; declare soh literally '1'; declare num_cmds literally '19'; declare num_para literally '17'; declare num_opt literally '8'; declare num_remote literally '10'; declare num_dup literally '2'; declare num_baud literally '7'; declare num_log literally '2'; declare cmd_list (num_cmds) structure (symbol(12) byte) data ('BYE ', 'CONNECT ', 'DEFINE ', 'EXIT ', 'FINISH ', 'GET ', 'HELP ', 'LOCAL ', 'LOG ', 'QUIT ', 'RECIEVE ', 'REMOTE ', 'SEND ', 'SERVER ', 'SET ', 'SHOW ', 'STATISTICS ', 'TAKE ', 'TRANSMIT '); declare para_list (num_para) structure (symbol(12) byte) data ('BAUD_RATE ', 'BLOCK_CHECK ', 'DEBUGGING ', 'DELAY ', 'DUPLEX ', 'ESCAPE ', 'FILE ', 'FLOW_CONTROL', 'HANDSHAKE ', 'IBM ', 'INCOMPLETE ', 'LINE ', 'PARITY ', 'PORT ', 'RECIEVE ', 'RETRY ', 'SEND '); declare opt_list (num_opt) structure (symbol(12) byte) DATA ('END_OF_LINE ', 'PACKET_LENGT', 'PADCHAR ', 'PADDING ', 'PAUSE ', 'QUOTE ', 'START_OF_PAC', 'TIMEOUT '); declare remote_list (num_remote) structure (symbol(12) byte) DATA ('CWD ', 'DELETE ', 'DIRECTORY ', 'DISK ', 'HELP ', 'HOST ', 'KERMIT ', 'RUN ', 'PROGRAM ', 'TYPE '); declare dup_list(num_dup) structure (symbol(12) byte) DATA ('FULL ', 'HALF '); declare baud_list(num_baud) structure (symbol(12) byte) DATA ('0 ', '300 ', '1200 ', '2400 ', '4800 ', '9600 ', '19200 '); declare log_list (num_log) structure (symbol(12) byte) data ('TRANSACTIONS', 'SESSION '); declare buflen literally '122'; declare buffer(buflen) byte EXTERNAL; declare cmdstr(buflen) byte EXTERNAL; declare status word EXTERNAL; declare baud_rate word EXTERNAL; declare duplex byte external; declare parity byte EXTERNAL; declare delim byte external; declare cmd byte external; declare in$conn token EXTERNAL; declare out$conn token EXTERNAL; declare ci$conn token EXTERNAL; declare co$conn token EXTERNAL; declare filename structure (len byte, name(80) byte) EXTERNAL; declare file$conn token EXTERNAL; declare debug byte EXTERNAL; declare qopen byte EXTERNAL; /* here are the subroutines */ $INCLUDE(:INC:UFLINF.EXT) $INCLUDE(:INC:UATACH.EXT) $INCLUDE(:INC:UOPEN.EXT) $INCLUDE(:INC:UCLOSE.EXT) $INCLUDE(:INC:UWRITE.EXT) $INCLUDE(:INC:UDCEX.EXT) $INCLUDE(:INC:UCREAT.EXT) $INCLUDE(:INC:UDCTIM.EXT) $INCLUDE(:INC:UDETAC.EXT) $INCLUDE(:INC:ISSPEC.EXT) $INCLUDE(:INC:USPECL.EXT) $INCLUDE(:INC:USWBF.EXT) $INCLUDE(:INC:UREAD.EXT) $INCLUDE(:INC:UEXIT.EXT) $INCLUDE(:INC:UGTARG.EXT) check$error: PROCEDURE (fatal) byte EXTERNAL; declare fatal byte; end check$error; nout: procedure(n) EXTERNAL; declare n word; end nout; nin: procedure(string) address EXTERNAL; declare string address; end nin; co: procedure(c) EXTERNAL; declare c byte; end co; do$co: procedure EXTERNAL; end do$co; newline: procedure EXTERNAL; end newline; prints: procedure(msg) EXTERNAL; declare msg pointer; end prints; print: procedure(msg) EXTERNAL; declare msg pointer; end print; file$close: procedure EXTERNAL; end file$close; query: procedure byte EXTERNAL; end query; print$char: procedure (char); declare char byte; filename.name(filename.len)=char; filename.len=filename.len+1; end print$char; print$str: procedure (ptr); declare ptr pointer; call movb(ptr,@filename.name(filename.len),12); filename.len=filename.len+12; end print$str; do$print$str: procedure; filename.name(filename.len)=cr; filename.name(filename.len+1)=lf; filename.len=filename.len+2; call prints(@filename); filename.len=0; end do$print$str; bye$cmd: procedure EXTERNAL; end bye$cmd; conn$cmd: procedure EXTERNAL; end conn$cmd; def$cmd: procedure EXTERNAL; end def$cmd; exit$cmd: procedure (code) EXTERNAL; declare code byte; end exit$cmd; fin$cmd: procedure EXTERNAL; end fin$cmd; get$cmd: procedure EXTERNAL; end get$cmd; help$cmd: procedure; if delim<>cr then do; delim=DQ$GET$ARGUMENT(@cmdstr,@status); if check$error(0) then return; cmd=decode$cmd(@cmdstr,@cmd_list,num_cmds); if cmd=true then call ambiguous; else do; do case cmd; call unknown(@(5,'Help ')); call bye$help; call conn$help; call def$help; call exit$help; call fin$help; call get$help; call help$help; call loc$help; call log$help; call quit$help; call recv$help; call rem$help; call send$help; call serv$help; call set$help; call show$help; call stat$help; call take$help; call tran$help; end; if cmd <> 0 then return; end; end; call help$help; end help$cmd; loc$cmd: procedure EXTERNAL; end loc$cmd; log$cmd: procedure EXTERNAL; end log$cmd; recv$cmd: procedure EXTERNAL; end recv$cmd; rem$cmd: procedure EXTERNAL; end rem$cmd; send$cmd: procedure EXTERNAL; end send$cmd; serv$cmd: procedure EXTERNAL; end serv$cmd; set$cmd: procedure EXTERNAL; end set$cmd; show$cmd: procedure EXTERNAL; end show$cmd; stat$cmd: procedure EXTERNAL; end stat$cmd; take$cmd: procedure EXTERNAL; end take$cmd; tran$cmd: procedure EXTERNAL; end tran$cmd; help$log$trans: procedure; call undocumented; end help$log$trans; help$log$session: procedure; call undocumented; end help$log$session; undocumented: procedure; call print(@('Help documentation not yet available',crlf)); end undocumented; ambiguous: procedure PUBLIC; call print(@('ambiguous command',crlf)); end ambiguous; unsupported: procedure PUBLIC; call print(@('not presently supported',crlf)); end unsupported; unknown: procedure(cmd$ptr) PUBLIC; declare cmd$ptr pointer; declare cmd based cmd$ptr structure (len byte, symbol(12) byte); call print(@('unknown ',null)); if cmd.len>0 then call prints(cmd$ptr); call print(@('command, check spelling',crlf)); end unknown; decode$cmd: procedure (cmd$ptr,list$ptr,num) byte PUBLIC; declare cmd$ptr pointer; declare list$ptr pointer; declare num byte; declare list based list$ptr (1) structure (symbol(12) byte); declare cmd based cmd$ptr structure (len byte, symbol(12) byte); declare (i,j,ix) byte; if debug then call prints(cmd$ptr); ix=0; if cmd.len>12 then cmd.len=12; else if cmd.len=0 then return ix; do i=1 to num; do j=1 to cmd.len; if cmd.symbol(j-1) <> list(i-1).symbol(j-1) then goto nexti; end; if ix<>0 then ix=true; else ix=i; nexti: end; if debug then call nout(ix); return ix; end decodecmd; do$cmd: procedure PUBLIC; do case cmd; call unknown(@(0)); call bye$cmd; call conn$cmd; call def$cmd; call exit$cmd(0); call fin$cmd; call get$cmd; call help$cmd; call loc$cmd; call log$cmd; call exit$cmd(0); call recv$cmd; call rem$cmd; call send$cmd; call serv$cmd; call set$cmd; call show$cmd; call stat$cmd; call take$cmd; call tran$cmd; end; end do$cmd; do$para: procedure PUBLIC; do case cmd; call unknown(@(10,'parameter ')); call baud$para; call block$para; call debug$para; call delay$para; call dup$para; call esc$para; call file$para; call flow$para; call hand$para; call ibm$para; call inco$para; call port$para; call par$para; call port$para; call recv$para; call retry$para; call send$para; end; end do$para; get$in$cmd: procedure PUBLIC; cmd=decode$cmd(@cmdstr,@cmd_list,num_cmds); if cmd=true then call ambiguous; else call do$cmd; end get$in$cmd; get$baud: procedure PUBLIC; cmd=decode$cmd(@cmdstr,@baud_list,num_baud); if cmd=true then call ambiguous; else do case cmd; call unknown(@(10,'baud rate ')); baud_rate=0; baud_rate=300; baud_rate=1200; baud_rate=2400; baud_rate=4800; baud_rate=9600; baud_rate=19200; end; end get$baud; get$para: procedure PUBLIC; cmd=decode$cmd(@cmdstr,@para_list,num_para); if cmd=true then call ambiguous; else call do$para; end get$para; output$baud: procedure PUBLIC; if cmd=1 then call print(@('system default',null)); else do; call co(baud_list(cmd-1).symbol(0)); call co(baud_list(cmd-1).symbol(1)); call co(baud_list(cmd-1).symbol(2)); call co(baud_list(cmd-1).symbol(3)); if cmd=7 then call co(baud_list(cmd-1).symbol(4)); end; end output$baud; get$duplex: procedure PUBLIC; cmd=decode$cmd(@cmdstr,@dup_list,num_dup); if cmd=true then call ambiguous; else do case cmd; call unknown(@(7,'duplex ')); duplex=0; duplex=1; end; end get$duplex; baud$para: procedure EXTERNAL; end baud$para; block$para: procedure EXTERNAL; end block$para; debug$para: procedure EXTERNAL; end debug$para; delay$para: procedure EXTERNAL; end delay$para; dup$para: procedure EXTERNAL; end dup$para; esc$para: procedure EXTERNAL; end esc$para; file$para: procedure EXTERNAL; end file$para; flow$para: procedure EXTERNAL; end flow$para; hand$para: procedure EXTERNAL; end hand$para; ibm$para: procedure EXTERNAL; end ibm$para; inco$para: procedure EXTERNAL; end inco$para; par$para: procedure EXTERNAL; end par$para; port$para: procedure EXTERNAL; end port$para; recv$para: procedure EXTERNAL; end recv$para; retry$para: procedure EXTERNAL; end retry$para; send$para: procedure EXTERNAL; end send$para; bye$help: procedure; call print(@('SYNTAX: Bye',crlf)); call print(@(lf,'Sends a message to remote kermit to exit from', ' server mode,',crlf)); call print(@(' and logout of remote system',crlf)); call print(@(' also exits from local program',crlf)); end bye$help; conn$help: procedure; call print(@('SYNTAX: Connect [device]',crlf)); call print(@(lf,'Makes a virtual terminal connection', ' via specified device',crlf)); call print(@(' if device not specified uses the one set up', ' by SET LINE command',crlf)); call print(@(' to break the connection type ^] C',crlf)); call print(@(lf,'SPECIAL NOTE: Because ^C is special for RMX,',crlf)); call print(@(' to send a control-C via the connection type ^] ^Y',crlf)); end conn$help; def$help: procedure; call print(@('SYNTAX: Define macroname [set-parameters]',crlf)); call undocumented; end def$help; exit$help: procedure; call print(@('SYNTAX: Exit',crlf)); call print(@(lf,'exits from program',crlf)); end exit$help; fin$help: procedure; call print(@('SYNTAX: Finish',crlf)); call print(@(lf,'Sends a message to remote kermit to exit from', ' server mode,',crlf)); call print(@(' and remote KERMIT but not logout of system',crlf)); end fin$help; get$help: procedure; call print(@('SYNTAX: Get filespec1 [filespec2]',crlf)); call print(@(lf,'filespec1 is remote filespec and may', ' have wildcards',crlf)); call print(@('filespec2 is local name to store file in,', ' no wildcard support',crlf)); end get$help; help$help: procedure; declare i byte; call print(@('Help is available on the following commands:',crlf)); call newline; filename.len=0; do i=0 to num_cmds-1; call print$str(@cmd_list(i)); if (i mod 5)=4 then call do$print$str; end; if (num_cmds mod 5)<>0 then call do$print$str; call newline; call print(@('Abreviations are allowed as long as', ' they are unique',crlf)); end help$help; loc$help: procedure; if delim<>cr then do; call undocumented; end; call print(@('SYNTAX: LOCal command',crlf)); end loc$help; log$help: procedure; if delim<>cr then do; delim=DQ$GET$ARGUMENT(@cmdstr,@status); if check$error(0) then return; cmd=decode$cmd(@cmdstr,@cmd_list,num_cmds); if cmd=true then call ambiguous; else do; do case cmd; call unknown(@(4,'Log ')); call help$log$trans; call help$log$session; end; if cmd <> 0 then return; end; end; call print(@('SYNTAX: LOG [option] [filespec]',crlf)); call print(@(' legal options are:',null)); filename.len=0; call print$str(@log_list(0)); call print$char(' '); call print$str(@log_list(1)); call do$print$str; call print(@(lf,'logs the specified option to the specified', ' log file',crlf)); call print(@(' if filespec is omitted, defaults to KERMIT.LOG', ' in the default directory',crlf)); end log$help; quit$help: procedure; call print(@('SYNTAX: Quit',crlf)); call print(@(lf,'exits from program',crlf)); end quit$help; recv$help: procedure; call print(@('SYNTAX: RECieve [filespec]',crlf)); call print(@(lf,'if filespec is missing or more than one', ' file is recieved,',crlf)); call print(@(' will use filespec from other computer.',crlf)); call print(@('No filename tranformation is available yet',crlf)); end recv$help; rem$help: procedure; if delim<>cr then do; call undocumented; end; call print(@('SYNTAX: REMote command',crlf)); end rem$help; send$help: procedure; call print(@('SYNTAX: SENd filespec1 [filespec2]',crlf)); call print(@(lf,'filespec1 may have wildcard parameters',crlf)); call print(@('filespec2 is not preently used.',crlf)); end send$help; serv$help: procedure; call print(@('SYNTAX: SERver',crlf)); call undocumented; end serv$help; set$help: procedure; declare i byte; if delim<>cr then do; call undocumented; end; call print(@('SYNTAX: SET parameter [option] [value]',crlf)); call print(@(lf,'Help is available on the following parameters:',crlf)); call newline; filename.len=0; do i=0 to num_para-1; call print$str(@para_list(i)); call print$char(' '); if (i mod 5)=4 then call do$print$str; end; if (num_para mod 5)<>0 then call do$print$str; call newline; end set$help; show$help: procedure; declare i byte; call print(@('SYNTAX: SHow [parameter]',crlf)); call print(@(lf,'If parameter is omitted,', ' all parameters are shown',crlf)); call print(@('The following are legal parameters:',crlf)); call newline; filename.len=0; do i=0 to num_para-1; call print$str(@para_list(i)); call print$char(' '); if (i mod 5)=4 then call do$print$str; end; if (num_para mod 5)<>0 then call do$print$str; call newline; end show$help; stat$help: procedure; call print(@('SYNTAX: STatistics',crlf)); call print(@(lf,'Gives statistics on the most recent transfer',crlf)); end stat$help; take$help: procedure; call print(@('SYNTAX: TAke filespec',crlf)); call print(@(lf,'Reads KERMIT commands from the specified file',crlf)); call print(@(' all commands except another TAKE command', ' are allowed',crlf)); end take$help; tran$help: procedure; call print(@('SYNTAX: TRansmit filespec',crlf)); call print(@(lf,'Sends a file without KERMIT protocall',crlf)); end tran$help; end cmds; /*---CONN.P86---*/ $compact $optimize(3) conn$module: do; /* CONNECT: Establish a "virtual terminal" connection through a */ /* specified serial i/o port. */ $INCLUDE(:INC:LTKSEL.LIT) declare status word external; declare (in$conn,out$conn) token external; declare (ci$conn,co$conn) token external; declare debug byte external; declare break literally '1DH'; declare ctly literally '19H'; /* ^C by typing ^]^Y */ declare ctlq literally '11H'; declare ctls literally '13H'; declare ctlc literally '03H'; declare true literally '0FFH'; declare false literally '00H'; declare null literally '0'; declare cr literally '0DH'; declare lf literally '0AH'; declare crlf literally 'cr,lf,null'; $INCLUDE(:INC:UREAD.EXT) $INCLUDE(:INC:UWRITE.EXT) declare iobuff(1024) byte public; check$error: procedure(fatal) byte external; declare fatal byte; end check$error; print: procedure(msg) external; declare msg pointer; end print; newline: procedure external; end newline; sbreak: procedure byte external; end sbreak; send$setup: procedure external; end send$setup; connect: procedure public; declare (c,i,qbreak) byte; qbreak=false; if debug then do; call print(@('connecting to serial port $')); /* something about which terminal line */ call newline; call print(@('to exit CONNECT mode type ^] C$')); call newline; end; do while (1); c=DQ$READ(ci$conn,@iobuff,80,@status); if check$error(0) then return; loop: if c>0 then do; if qbreak then do; qbreak=false; if iobuff(0)='C' then return; else if iobuff(0)='c' then return; else if iobuff(0)=ctly then iobuff(0)=ctlc; else if iobuff(0)='0' then iobuff(0)=null; else if iobuff(0)='?' then do; call print(@('Special characters are: B,C,?,^Y,0',crlf)); c=c-1; if i>0 then call movb(@iobuff(1),@iobuff(0),c); goto loop; end; else if (iobuff(0)='B' or iobuff(0)='b') then do; call send$setup; i=sbreak; c=c-1; if i>0 then call movb(@iobuff(1),@iobuff(0),c); goto loop; end; /* add check for other characters.....otherwise ignore */ end; do i=0 to c-1; if iobuff(i)=break then do; if i>0 then do; call DQ$WRITE(out$conn,@iobuff,i,@status); if check$error(0) then return; end; c=c-i-1; if c>0 then call movb(@iobuff(i+1),@iobuff,c); qbreak=true; goto loop; end; end; call DQ$WRITE(out$conn,@iobuff,c,@status); if check$error(0) then return; end; c=DQ$READ(in$conn,@iobuff,40,@status); if check$error(0) then return; if c>0 then do; call DQ$WRITE(co$conn,@iobuff,c,@status); if check$error(0) then return; end; end; end connect; end conn$module; /*---GET.P86---*/ /* GET: routine to get a file from a remote kermit in server mode also includes BYE and FINISH cmds. */ $compact $optimize(3) get$module: do; $include(:INC:LTKSEL.LIT) declare true literally '0FFH'; declare false literally '00H'; declare null literally '00'; declare cr literally '0DH'; declare lf literally '0AH'; declare crlf literally 'cr,lf,null'; declare myquote literally '023H'; declare chrmsk literally '07FH'; declare state byte; declare tries byte; declare msgnum byte; declare maxtry literally '5'; declare eol byte; declare debug byte external; declare iobuff(1024) byte external; declare status word external; declare pksize literally '94'; declare send$packet(pksize) byte external; declare recv$packet(pksize) byte external; declare count word; declare oldtry byte; declare byte$in dword; declare file$conn token external; declare filename structure (len byte, name(80) byte) external; declare qopen byte external; declare dummy byte; $include(:INC:USWBF.EXT) $include(:INC:UGTARG.EXT) check$error: procedure(mode) byte external; declare mode byte; end check$error; file$open: procedure(mode) external; declare mode byte; end file$open; file$close: procedure external; end file$close; co: procedure(char)external; declare char byte; end co; print: procedure(string)external; declare string pointer; end print; nout: procedure(num)external; declare num word; end nout; noutd: procedure(num)external; declare num dword; end noutd; newline: procedure external; end newline; ctl: procedure(char) byte external; declare char byte; end ctl; putc: procedure (c,conn) external; declare c byte; declare conn token; end putc; do$put: procedure (conn) external; declare conn token; end do$put; spack: procedure(type, pknum, length, packet) external; declare (type, pknum, length) byte; declare packet address; end spack; rpack: procedure(length, pknum, packet) byte external; declare (length, pknum, packet) address; end rpack; spar: procedure (a) external; declare a address; end spar; rpar: procedure (a) external; declare a address; end rpar; rinit: procedure byte external; end rinit; rfile: procedure byte external; end rfile; rdata: procedure byte external; end rdata; recv$setup: procedure external; end recv$setup; ginit: procedure byte; call spack('R',msgnum,filename.len,.filename.name); state=rinit; return state; end ginit; get: procedure byte public; if debug then call print(@('Get a file',crlf)); state = 'R'; msgnum = 0; call recv$setup; do while true; if state = 'D' then state = rdata; else if state = 'F' then state = rfile; else if state = 'R' then state = ginit; else if state = 'C' then return true; else return false; end; end get; bye: procedure byte public; declare (num,length,retc) byte; tries=0; retc='N'; msgnum=0; do while (retc<>'Y'); if tries>maxtry then return false; call spack('G',msgnum,1,.('L')); retc = rpack(.length, .num, .recv$packet); tries=tries+1; end; return true; end bye; finish: procedure byte public; declare (num,length,retc) byte; tries=0; retc='N'; msgnum=0; do while (retc<>'Y'); if tries>maxtry then return false; call spack('G',msgnum,1,.('F')); retc = rpack(.length, .num, .recv$packet); tries=tries+1; end; return true; end finish; end get$module; /*---KERMIT.P86---*/ $compact $optimize(3) kermit: do; declare true literally '0FFH'; declare false literally '00H'; $INCLUDE(:INC:LTKSEL.LIT) $INCLUDE(:INC:NEXCEP.LIT) $INCLUDE(:INC:IEXCEP.LIT) declare null literally '000H'; declare lf literally '0AH'; declare cr literally '0DH'; declare crlf literally 'cr,lf,null'; declare space literally '20H'; declare dollar literally '24H'; declare soh literally '1'; declare term$attr structure (num$words word, num$used word, connect$flag word, terminal$flag word, in$baud$rate word, out$baud$rate word, scroll$lines word, x$y$size word, x$y$offset word, flow$control word, high$water$mark word, low$water$mark word, fc$on$char word, fc$off$char word); declare fdata structure( len$owner byte, owner(14) byte, length dword, type byte, owner$access byte, world$access byte, create$time dword, last$mod$time dword, reserved(20) byte); declare file$len (2) word PUBLIC AT (@fdata.length); declare file$truncate byte; declare buflen literally '122'; declare buffer(buflen) byte PUBLIC; declare outbuf(buflen) byte; declare takebuf(buflen) byte; declare cmdstr(buflen) byte PUBLIC; declare query_in(10) byte; declare outlen word; declare trans_wait word public; declare status word public; declare old_baud_in word; declare old_baud_ci word; declare dev_attach byte; declare server$mode byte public; declare baud_rate word PUBLIC; declare block_check byte public; declare duplex byte PUBLIC; declare break_char byte public; declare parity byte public; declare delim byte public; declare len word; declare send$delay byte public; declare send$eol byte public; declare send$paclen byte public; declare send$padchar byte public; declare send$padding byte public; declare send$pause byte public; declare send$quote byte public; declare send$start byte public; declare send$time byte public; declare recv$eol byte public; declare recv$paclen byte public; declare recv$padchar byte public; declare recv$padding byte public; declare recv$pause byte public; declare recv$quote byte public; declare recv$start byte public; declare recv$time byte public; declare send$setup$string(6) byte public; declare cmd byte public; declare in$conn token public; declare out$conn token public; declare ci$conn token public; declare co$conn token public; declare filestr structure (len byte, name(80) byte); declare filename structure (len byte, name(80) byte) public; declare file$conn token public; declare takename structure (len byte, name(80) byte); declare take$conn token; declare takelen byte initial (0); declare takeindex byte initial (0); declare debug byte public; declare qopen byte public; declare iobuff(1024) byte external; /* here are the subroutines */ $INCLUDE(:INC:HGTIPN.EXT) $INCLUDE(:INC:HSTPBF.EXT) $INCLUDE(:INC:UFLINF.EXT) $INCLUDE(:INC:UATACH.EXT) $INCLUDE(:INC:UOPEN.EXT) $INCLUDE(:INC:UCLOSE.EXT) $INCLUDE(:INC:UWRITE.EXT) $INCLUDE(:INC:UDCEX.EXT) $INCLUDE(:INC:UCREAT.EXT) $INCLUDE(:INC:UDCTIM.EXT) $INCLUDE(:INC:UDETAC.EXT) $INCLUDE(:INC:ISSPEC.EXT) $INCLUDE(:INC:USPECL.EXT) $INCLUDE(:INC:USWBF.EXT) $INCLUDE(:INC:UREAD.EXT) $INCLUDE(:INC:UEXIT.EXT) $INCLUDE(:INC:UGTARG.EXT) $INCLUDE(:INC:UTRUNC.EXT) connect: procedure external; end connect; spar: procedure (a) external; declare a address; end spar; rpar: procedure (a) external; declare a address; end rpar; do$put: procedure(conn) external; declare conn token; end do$put; send: procedure byte external; end send; bye: procedure byte external; end bye; finish: procedure byte external; end finish; get: procedure byte external; end get; recv: procedure byte external; end recv; trans: procedure byte external; end trans; check$error: PROCEDURE (fatal) byte PUBLIC; declare fatal byte; declare dummy word; declare exc$buf structure( count byte, char(80) byte); if status <> E$OK then do; call DQ$DECODE$EXCEPTION(status,@exc$buf,@dummy); call DQ$WRITE(co$conn,@exc$buf.char,exc$buf.count,@dummy); call DQ$WRITE(co$conn,@(cr,lf),2,@dummy); if fatal<>0 then call exit$cmd(3); return true; end; return false; end check$error; declare digit word; declare numbuf(20) byte; declare index byte; nout: procedure(n) public; declare n word; if n = 0 then do; call co('0'); return; end; index = 1; do while (n > 0); digit = n mod 10; numbuf(index) = digit+030H; index = index + 1; n = n / 10; end; do while ((index := index - 1) > 0); call co(numbuf(index)); end; end nout; noutd: procedure(n) public; declare n dword; if n = 0 then do; call co('0'); return; end; index = 1; do while (n > 0); digit = n mod 10; numbuf(index) = digit+030H; index = index + 1; n = n / 10; end; do while ((index := index - 1) > 0); call co(numbuf(index)); end; end noutd; nin: procedure(string) address public; declare string address; declare result address; declare c based string byte; result = 0; if (string <> 0) then do; do while (c >= 030H) and (c <= 039H); result = result * 10 + (c - 030H); string = string + 1; end; end; return result; end nin; co: procedure(c) public; declare c byte; outbuf(outlen)=c; outlen=outlen+1; if outlen>50 then do; call DQ$WRITE(co$conn,@outbuf,outlen,@status); if check$error(1) then return; outlen=0; end; end co; do$co: procedure public; if outlen>0 then do; call DQ$WRITE(co$conn,@outbuf,outlen,@status); if check$error(1) then return; outlen=0; end; return; end do$co; newline: procedure public; outbuf(outlen)=cr; outbuf(outlen+1)=lf; call DQ$WRITE(co$conn,@outbuf,outlen+2,@status); if check$error(1) then return; outlen=0; end newline; prints: procedure(msg) public; declare msg pointer; declare buff BASED msg structure (len byte, msg byte); call do$co; call DQ$WRITE(co$conn,@buff.msg,buff.len,@status); if check$error(1) then return; return; end prints; print: procedure(msg) public; declare (msg,oldmsg) pointer; declare c based msg (1) byte; declare i word; call do$co; oldmsg=msg; i=0; do while (c(i) > 0) and (c(i) <> '$'); if c(i) = '\' then do; if i>0 then do; call DQ$WRITE(co$conn,oldmsg,i,@status); if check$error(1) then return; end; call DQ$WRITE(co$conn,@(cr,lf),2,@status); if check$error(1) then return; oldmsg=@c(i+1); i=0; msg=oldmsg; end; else i=i+1; end; if i>0 then do; call DQ$WRITE(co$conn,oldmsg,i,@status); if check$error(1) then return; end; end print; set$term$attr: procedure(qdefault); declare qdefault byte; declare c byte; declare save$conn$flag word; declare save$term$flag word; if qdefault then do; /* here restore normal terminal attributes */ term$attr.connect$flag=save$conn$flag; term$attr.terminal$flag=save$term$flag; end; else do; /* here set kermit terminal attributes */ save$conn$flag=term$attr.connect$flag; save$term$flag=term$attr.terminal$flag; term$attr.connect$flag=term$attr.connect$flag OR 7; if parity=4 then do; term$attr.connect$flag=term$attr.connect$flag OR 18H; term$attr.terminal$flag=(term$attr.terminal$flag OR 1F0H) xor 0E0H; end; else call print(@('Unsupported parity specified',crlf)); if duplex then term$attr.terminal$flag=term$attr.terminal$flag OR 2; else term$attr.terminal$flag=term$attr.terminal$flag AND 0FFFDH; end; call RQ$S$SPECIAL(in$conn,5,@term$attr,0,@status); if check$error(1) then return; if NOT qdefault then do; /* PURGE ANY INPUT QUEUED UP */ c=1; do while c<>0; c=DQ$READ(in$conn,@iobuff,127,@status); if check$error(1) then return; end; end; end set$term$attr; get$term$attr: procedure; call RQ$S$SPECIAL(in$conn,4,@term$attr,0,@status); if check$error(1) then return; if debug then do; call print(@('conn_flag ',null)); call nout(term$attr.connect$flag); call print(@(' term_flag ',null)); call nout(term$attr.terminal$flag); call newline; call print(@('baud rate in/out ',null)); call nout(term$attr.in$baud$rate); call co(' '); call nout(term$attr.out$baud$rate); call newline; call print(@('flow control ',null)); call nout(term$attr.flow$control); call newline; end; return; end get$term$attr; /* IOINIT: */ ioinit: procedure; ci$conn=DQ$ATTACH(@(4,':CI:'),@status); co$conn=DQ$ATTACH(@(4,':CO:'),@status); call DQ$OPEN(ci$conn,1,2,@status); call DQ$OPEN(co$conn,2,0,@status); if debug then CALL DQ$WRITE(co$conn, @('openned consol for I/O',cr,lf),24,@status); in$conn=ci$conn; out$conn=co$conn; call get$term$attr; call print(@('Default communication thru :CI:/:CO:',crlf)); end ioinit; file$open: procedure (mode) PUBLIC; declare mode byte; file$conn=DQ$ATTACH(@filename,@STATUS); file$truncate=false; if mode=2 then do; if status=E$FNEXIST then file$conn=DQ$CREATE(@filename,@status); else if status=E$OK then do; call print(@('About to overwrite file ',null)); call prints(@filename); call print(@(', please confirm',null)); if NOT query then return; file$truncate=true; end; end; if check$error(0) then return; call DQ$OPEN(file$conn,mode,2,@status); if check$error(0) then return; if mode=1 then do; call DQ$FILE$INFO(file$conn,0,@fdata,@status); if check$error(0) then return; end; qopen=true; return; end file$open; file$close: procedure public; if qopen then do; if file$truncate then do; call DQ$TRUNCATE(file$conn,@status); if check$error(0) then return; end; call DQ$CLOSE(file$conn,@status); if check$error(0) then return; call DQ$DETACH(file$conn,@status); if check$error(0) then return; qopen=false; end; end file$close; return$to$ci: procedure; if in$conn <> ci$conn then do; call close$in; in$conn=ci$conn; out$conn=co$conn; call get$term$attr; old_baud_in=term$attr.in$baud$rate; call print(@('set connection via :CI:/:CO:',crlf)); if baud_rate<>0 then do; if term$attr.in$baud$rate<>baud_rate then do; call print(@('you are about to change the CI/CO baud rate', ', please confirm:',null)); if query then do; term$attr.in$baud$rate=baud_rate; call RQ$S$SPECIAL(in$conn,5,@term$attr,0,@status); if check$error(1) then return; end; else baud_rate=0; end; end; end; end return$to$ci; close$in: procedure; if baud_rate <> 0 then do; if term$attr.in$baud$rate <> old_baud_in then do; term$attr.in$baud$rate=old_baud_in; call RQ$S$SPECIAL(in$conn,5,@term$attr,0,@status); if check$error(1) then return; end; end; call DQ$CLOSE(in$conn,@status); if check$error(0) then return; call DQ$DETACH(in$conn,@status); if check$error(0) then return; end close$in; query: procedure byte public; cmd=DQ$READ(ci$conn,@query_in,10,@status); if check$error(0) then return false; if query_in(0)='y' or query_in(0)='Y' then return true; return false; end query; get$line: procedure byte; declare i byte; len=0; takeindex=takeindex+1; loop: if takeindex>=takelen then do; takelen=DQ$READ(take$conn,@takebuf,120,@status); if check$error(0) then return 0; takeindex=0; if takelen=0 then return 0; end; do i=takeindex to takelen-1; buffer(len)=takebuf(i); if debug then call co(takebuf(i)); if takebuf(i) <> lf then len=len+1; if takebuf(i)=cr then do; if debug then call do$co; takeindex=i; return len; end; end; takeindex=takelen; goto loop; end get$line; readln: procedure; declare len word; len=DQ$READ(ci$conn,@buffer,120,@status); if check$error(1) then return; len=DQ$SWITCH$BUFFER(@buffer,@status); if check$error(1) then return; end readln; bye$cmd: procedure PUBLIC; if in$conn=ci$conn then do; call print(@('can not send bye to yourself...use SET cmd first', crlf)); return; end; call set$term$attr(false); if bye then call exit$cmd(3); else call print(@('Error shutting down remote KERMIT',crlf)); call set$term$attr(true); end bye$cmd; conn$cmd: procedure PUBLIC; if delim<>cr then call port$para; if in$conn=ci$conn then do; call print(@('can not connect to yourself...use SET cmd first', crlf)); return; end; call DQ$SPECIAL(3,@ci$conn,@status); if check$error(1) then return; call set$term$attr(false); if term$attr.in$baud$rate>4000 then call print(@('Warning..at present BAUD rate characters', ' will be lost during BURST transmitions',crlf)); call connect; call set$term$attr(true); call DQ$SPECIAL(2,@ci$conn,@status); if check$error(1) then return; call newline; end conn$cmd; def$cmd: procedure PUBLIC; call unsupported; end def$cmd; exit$cmd: procedure(code) public; declare code byte; /* clean up terminal attr. */ call DQ$EXIT(code); end exit$cmd; fin$cmd: procedure PUBLIC; if in$conn=ci$conn then do; call print(@('can not send finish to yourself...use SET cmd first', crlf)); return; end; call set$term$attr(false); if NOT finish then call print(@('Error ending remote KERMIT server',crlf)); call set$term$attr(true); end fin$cmd; get$cmd: procedure PUBLIC; if delim = cr then call print(@('No files specified',crlf)); else do; delim=DQ$GET$ARGUMENT(@filename,@status); if check$error(0) then return; /* HERE IS WHERE YOU SET UP WILDCARD NAMES USING FILESTR */ call file$open(2); if qopen then do; call set$term$attr(false); if get then call print(@(cr,lf,'OK',crlf)); else call print(@('get failed',crlf)); call set$term$attr(true); end; call file$close; end; end get$cmd; loc$cmd: procedure PUBLIC; call unsupported; end loc$cmd; log$cmd: procedure PUBLIC; call unsupported; end log$cmd; recv$cmd: procedure PUBLIC; if delim <> cr then do; delim=DQ$GET$ARGUMENT(@filename,@status); if check$error(0) then return; call file$open(2); end; call set$term$attr(false); if recv then call print(@(cr,lf,'OK',crlf)); else call print(@(cr,lf,'error recieving file',crlf)); call set$term$attr(true); call do$put(file$conn); call file$close; end recv$cmd; rem$cmd: procedure PUBLIC; call unsupported; end rem$cmd; send$cmd: procedure PUBLIC; if delim = cr then call print(@('No files specified',crlf)); else do; delim=DQ$GET$ARGUMENT(@filename,@status); if check$error(0) then return; /* HERE IS WHERE YOU SET UP WILDCARD NAMES USING FILESTR */ call file$open(1); /* add check for output file spec */ if qopen then do; call set$term$attr(false); if send then call print(@(cr,lf,'OK',crlf)); else call print(@('Send failed',crlf)); call set$term$attr(true); end; call file$close; end; end send$cmd; serv$cmd: procedure PUBLIC; call unsupported; end serv$cmd; set$cmd: procedure PUBLIC; if delim = cr then call print(@('No parameter specified',crlf)); else do; delim=DQ$GET$ARGUMENT(@cmdstr,@status); if check$error(0) then return; call get$para; end; end set$cmd; get$para: procedure EXTERNAL; end get$para; get$in$cmd: procedure EXTERNAL; end get$in$cmd; show$cmd: procedure PUBLIC; call unsupported; end show$cmd; stat$cmd: procedure PUBLIC; call unsupported; end stat$cmd; take$cmd: procedure PUBLIC; declare i byte; if delim = cr then call print(@('No file specified',crlf)); else do; delim=DQ$GET$ARGUMENT(@takename,@status); if check$error(0) then return; take$conn=DQ$ATTACH(@takename,@STATUS); if check$error(0) then return; call DQ$OPEN(take$conn,1,2,@status); if check$error(0) then return; /* here is where you read cmd file, line by line */ do while get$line <> 0; i=DQ$SWITCH$BUFFER(@buffer,@status); if check$error(1) then return; delim=DQ$GET$ARGUMENT(@cmdstr,@status); if check$error(0) then return; if cmdstr(0)>0 then call get$in$cmd; end; call DQ$CLOSE(take$conn,@status); if check$error(0) then return; call DQ$DETACH(take$conn,@status); if check$error(0) then return; end; end take$cmd; tran$cmd: procedure PUBLIC; if delim = cr then call print(@('No files specified',crlf)); else do; delim=DQ$GET$ARGUMENT(@filename,@status); if check$error(0) then return; /* HERE IS WHERE YOU SET UP WILDCARD NAMES USING FILESTR */ call file$open(1); if qopen then do; call print(@('Please enter wait interval between 64', ' byte bursts',crlf)); call readln; delim=DQ$GET$ARGUMENT(@cmdstr,@status); if check$error(0) then return; cmdstr(cmdstr(0))=delim; trans_wait=nin(.cmdstr(1)); call set$term$attr(false); if trans then call print(@(cr,lf,'OK',crlf)); else call print(@('Transmit failed',crlf)); call set$term$attr(true); end; call file$close; end; end tran$cmd; ambiguous: procedure EXTERNAL; end ambiguous; unsupported: procedure EXTERNAL; end unsupported; unknown: procedure(cmd$ptr) EXTERNAL; declare cmd$ptr pointer; end unknown; do$cmd: procedure EXTERNAL; end do$cmd; do$para: procedure EXTERNAL; end do$para; get$baud: procedure EXTERNAL; end get$baud; get$duplex: procedure EXTERNAL; end get$duplex; output$baud: procedure EXTERNAL; end output$baud; baud$para: procedure PUBLIC; if delim=cr then do; baud_rate=0; end; else do; delim=DQ$GET$ARGUMENT(@cmdstr,@status); if check$error(0) then return; call get$baud; if cmd<=0 then return; if in$conn=ci$conn then do; call print(@('about to change consol baud rate to ',null)); call output$baud; call print(@(', please confirm:',null)); if NOT query then return; end; end; if baud_rate=0 then term$attr.in$baud$rate=old_baud_in; else term$attr.in$baud$rate=baud_rate; call RQ$S$SPECIAL(in$conn,5,@term$attr,@buffer,@status); if check$error(1) then return; end baud$para; block$para: procedure PUBLIC; call unsupported; end block$para; debug$para: procedure PUBLIC; debug= NOT debug; if debug then call print(@('DEBUG ON',crlf)); else call print(@('DEBUG OFF',crlf)); end debug$para; delay$para: procedure PUBLIC; if delim=cr then send$delay=5; else do; delim=DQ$GET$ARGUMENT(@cmdstr,@status); if check$error(0) then return; cmdstr(cmdstr(0))=delim; send$delay=nin(.cmdstr(1)); end; end delay$para; dup$para: procedure PUBLIC; if delim=cr then duplex=0; else do; delim=DQ$GET$ARGUMENT(@cmdstr,@status); if check$error(0) then return; call get$duplex; end; end dup$para; esc$para: procedure PUBLIC; call unsupported; end esc$para; file$para: procedure PUBLIC; call unsupported; end file$para; flow$para: procedure PUBLIC; call unsupported; end flow$para; hand$para: procedure PUBLIC; call unsupported; end hand$para; ibm$para: procedure PUBLIC; call unsupported; end ibm$para; inco$para: procedure PUBLIC; call unsupported; end inco$para; par$para: procedure PUBLIC; call unsupported; end par$para; port$para: procedure PUBLIC; if delim=cr then call return$to$ci; else do; delim=DQ$GET$ARGUMENT(@cmdstr,@status); if check$error(0) then return; if cmdstr(0)<>4 or (CMPB(@cmdstr(1),@(':CI:'),4)<>-1 and CMPB(@cmdstr(1),@(':CO:'),4)<>-1) then do; if in$conn <> ci$conn then call close$in; in$conn=DQ$ATTACH(@cmdstr,@status); if check$error(0) then return; call DQ$OPEN(in$conn,3,0,@status); if check$error(0) then return; out$conn=in$conn; call get$term$attr; old_baud_in=term$attr.in$baud$rate; if baud_rate <> 0 then do; /* set new terminal to requested baud rate */ end; call print(@('set connection via ',null)); call prints(@cmdstr); call newline; end; else call return$to$ci; end; call get$term$attr; end port$para; recv$para: procedure PUBLIC; call unsupported; end recv$para; retry$para: procedure PUBLIC; call unsupported; end retry$para; send$para: procedure PUBLIC; call unsupported; end send$para; /* *** main program *** */ outlen=0; debug = false; server$mode=false; dev_attach=false; qopen = false; send$delay=5; send$eol=cr; recv$eol=cr; send$paclen=94; recv$paclen=94; send$padchar=0; recv$padchar=0; send$padding=0; recv$padding=0; send$pause=1; recv$pause=1; send$quote=23H; recv$quote=23H; send$start=soh; recv$start=soh; send$time=5; recv$time=5; baud_rate=0; /* use system default */ block_check=1; /* simple check-sum */ duplex=0; /* 0=FULL, 1=HALF */ break_char=1DH; /* default ^] */ parity=4; /* parity code 0, set to 0 on output ignore on input, but clear bit 7 1, set to 1 on output ignore on input, but clear bit 7 2, even parity in and out 3, odd parity in and out 4, 8-bit...do not check or change bit 7 */ term$attr.num$words=5; term$attr.num$used=5; call spar(.send$setup$string); call rpar(.send$setup$string); call ioinit; old_baud_ci=term$attr.in$baud$rate; old_baud_in=0; call print(@('RMX-86 Kermit Version 1.0',crlf)); do while (true); call print(@('Kermit-RMX>',null)); call readln; delim=DQ$GET$ARGUMENT(@cmdstr,@status); if check$error(1) then call exit$cmd(3); if cmdstr(0)>0 then call get$in$cmd; end; end kermit; /*---RECV.P86---*/ /* RECEIVE: Routines for reading from the console and the serial ports */ $compact $optimize(3) recv$module: do; $include(:INC:LTKSEL.LIT) declare true literally '0FFH'; declare false literally '00H'; declare null literally '00'; declare cr literally '0DH'; declare lf literally '0AH'; declare crlf literally 'cr,lf,null'; declare myquote literally '023H'; declare chrmsk literally '07FH'; declare state byte; declare tries byte; declare msgnum byte; declare maxtry literally '5'; declare eol byte; declare debug byte external; declare iobuff(1024) byte external; declare status word external; declare pksize literally '94'; declare send$packet(pksize) byte external; declare recv$packet(pksize) byte external; declare count word; declare oldtry byte; declare byte$in dword; declare file$conn token external; declare filename structure (len byte, name(80) byte) external; declare qopen byte external; declare dummy byte; $include(:INC:USWBF.EXT) $include(:INC:UGTARG.EXT) check$error: procedure(mode) byte external; declare mode byte; end check$error; file$open: procedure(mode) external; declare mode byte; end file$open; file$close: procedure external; end file$close; co: procedure(char)external; declare char byte; end co; print: procedure(string)external; declare string pointer; end print; nout: procedure(num)external; declare num word; end nout; noutd: procedure(num)external; declare num dword; end noutd; newline: procedure external; end newline; ctl: procedure(char) byte external; declare char byte; end ctl; putc: procedure (c,conn) external; declare c byte; declare conn token; end putc; do$put: procedure (conn) external; declare conn token; end do$put; spack: procedure(type, pknum, length, packet) external; declare (type, pknum, length) byte; declare packet address; end spack; rpack: procedure(length, pknum, packet) byte external; declare (length, pknum, packet) address; end rpack; spar: procedure (a) external; declare a address; end spar; rpar: procedure (a) external; declare a address; end rpar; bufemp: procedure(packet, len); declare packet address; declare inchar based packet byte; declare (i, char, len) byte; if debug then call print(@('Writing to disk...',null)); i = 0; do while (i < len); char = inchar; if char = myquote then do; packet = packet + 1; i = i + 1; char = inchar; if (char and chrmsk) <> myquote then char = ctl(char); end; if debug then call co(char); call putc(char,file$conn); packet = packet + 1; byte$in=byte$in+1; i = i + 1; end; if debug then call newline; call do$put(file$conn); end bufemp; rinit: procedure byte public; declare (len, num, retc) byte; if tries > maxtry then return 'A'; else tries = tries + 1; if debug then call print(@('rinit...',crlf)); retc = rpack(.len, .num, .recv$packet); if (retc <> 'S') then return state; /* here on send init received */ call rpar(.recv$packet); call spar(.send$packet); call spack('Y', msgnum, 6, .send$packet); oldtry = tries; tries = 0; byte$in=0; msgnum = (msgnum + 1) mod 64; return 'F'; end rinit; rfile: procedure byte public; declare (len, num, retc) byte; if tries > maxtry then return 'A'; else tries = tries + 1; if debug then call print(@('rfile...',crlf)); retc = rpack(.len, .num, .recv$packet); if retc = 'S' then do; if (oldtry > maxtry) then return 'A'; else oldtry = oldtry + 1; if (num = msgnum - 1) then do; call spar(.send$packet); call spack('Y', num, 6 , .send$packet); tries = 0; return state; end; else return 'A'; end; if retc = 'Z' then do; if (oldtry > maxtry) then return 'A'; else oldtry = oldtry + 1; if (num = msgnum - 1) then do; call spack('Y', num, 0, 0); tries = 0; return state; end; else return 'A'; end; if retc = 'F' then do; if (num <> msgnum) then return 'A'; call print(@(cr,lf,'Receiving ',null)); call print(@recv$packet); call newline; if not qopen then do; dummy=DQ$SWITCH$BUFFER(@recv$packet,@status); if check$error(0) then return 'A'; dummy=DQ$GET$ARGUMENT(@filename,@status); if check$error(0) then return 'A'; call file$open(2); end; if not qopen then return 'A'; call spack('Y', msgnum, 0, 0); oldtry = tries; tries = 0; msgnum = (msgnum + 1) mod 64; return 'D'; end; if retc = 'B' then do; if (num <> msgnum) then return 'A'; call spack('Y', msgnum, 0, 0); return 'C'; end; return state; end rfile; rdata: procedure byte public; declare (num, len, retc) byte; if tries > maxtry then return 'A'; else tries = tries + 1; if debug then call print(@('rdata...',crlf)); retc = rpack(.len, .num, .recv$packet); if retc = 'D' then do; if (num <> msgnum) then do; if (oldtry > maxtry) then return 'A'; else oldtry = oldtry + 1; if (num = msgnum - 1) then do; call spar(.send$packet); call spack('Y', num, 6, .send$packet); tries = 0; return state; end; else return 'A'; end; call bufemp(.recv$packet, len); call spack('Y', msgnum, 0, 0); oldtry = tries; tries = 0; call print(@('recieved ',null)); call noutd(byte$in); call print(@(' bytes ',cr,null)); msgnum = (msgnum + 1) mod 64; return 'D'; end; if retc = 'F' then do; if (oldtry > maxtry) then return 'A'; else oldtry = oldtry + 1; if (num = msgnum - 1) then do; call spack('Y', num, 0, 0); tries = 0; return state; end; else return 'A'; end; if retc = 'Z' then do; if (num <> msgnum) then return 'A'; call spack('Y', msgnum, 0, 0); call file$close; msgnum = (msgnum + 1) mod 64; return 'F'; end; call spack('N', msgnum, 0, 0); return state; end rdata; recv$setup: procedure public; state = 'R'; msgnum = 0; tries = 0; oldtry = 0; end recv$setup; recv: procedure byte public; if debug then call print(@('Receive a file',crlf)); call recv$setup; do while true; if state = 'D' then state = rdata; else if state = 'F' then state = rfile; else if state = 'R' then state = rinit; else if state = 'C' then return true; else return false; end; end recv; end recv$module; /*---SEND.P86---*/ /* SEND MODULE: this module handles all sending of data between the */ /* host and RMX system */ $compact $optimize(3) send$module: do; $INCLUDE(:INC:LTKSEL.LIT) $INCLUDE(:INC:UREAD.EXT) $INCLUDE(:INC:UWRITE.EXT) $INCLUDE(:INC:UDCTIM.EXT) $INCLUDE(:INC:NSLEEP.EXT) /* here are some global declarations for the communication module */ declare true literally '0FFH'; declare false literally '00H'; declare chrmsk literally '07FH'; declare maxtry literally '05'; declare space literally '020H'; declare cr literally '0DH'; declare lf literally '0AH'; declare null literally '00H'; declare crlf literally 'cr,lf,null'; declare eofl literally '0'; declare delete literally '07FH'; declare send$delay byte external; declare send$eol byte external; declare send$paclen byte external; declare send$padchar byte external; declare send$padding byte external; declare send$pause byte external; declare send$quote byte external; declare send$start byte external; declare send$time byte external; declare readonly literally '1'; declare writeonly literally '2'; declare rdwr literally '3'; declare noedit literally '0'; declare pksize literally '94'; declare send$packet(pksize) byte public; /* buffer for packets */ declare recv$packet(pksize) byte public; /* buffer for packets */ declare send_delay word; declare state byte; /* FSM last state */ declare msgnum byte; /* message number */ declare tries byte; /* max number of retries */ declare numpads byte; /* how many pads to send */ declare padchar byte; /* the present pad character */ declare eol byte; /* the present eol character */ declare quote byte; /* the present quote character */ declare timeint byte; /* the present time out */ declare spsize byte; /* the present packet size */ declare pklen word; declare (j,count) word initial (0,0); declare (k,cnt) word initial (0,0); declare buflen literally '128'; declare inbuf (buflen) byte; declare outbuf(buflen) byte; declare outlen word initial (0); declare (in$conn,out$conn) token external; declare (ci$conn,co$conn) token external; declare status word external; declare debug byte external; declare file$conn token external; declare iobuff(1024) byte external; declare file$len (2) word external; declare byte$out dword; declare byte$tot dword at (@file$len); declare frac$tot word; declare filename structure (len byte, name(80) byte) external; declare wait$time byte public; declare system$end$time dword public; declare time$buffer structure (system$time dword, date(8) byte, time(8) byte); /* here are the subroutines */ check$error: procedure (fatal) byte external; declare fatal byte; end check$error; co: procedure(char) external; declare char byte; end co; prints: procedure(msg) external; declare msg pointer; end prints; print: procedure(string) external; declare string pointer; end print; nout: procedure(n) external; declare n word; end nout; noutd: procedure(n) external; declare n dword; end noutd; file$open: procedure (mode) external; declare mode byte; end file$open; newline: procedure external; end newline; /* TOCHAR: takes a character and converts it to a printable character */ /* by adding a space */ tochar: procedure(char) byte public; declare char byte; return (char + space); end tochar; /* UNCHAR: undoes 'tochar' */ unchar: procedure(char) byte public; declare char byte; return (char - space); end unchar; /* CTL: this routine takes a character and toggles the control bit */ /* (ie. ^A becomes A and A becomes ^A). */ ctl: procedure(char) byte public; declare char byte; declare cntrlbit literally '040H'; return (char xor cntrlbit); end ctl; getc: procedure (conn) byte public; declare conn token; if debug then call print(@('Entering getc...',crlf)); k=k+1; loop: if k>=cnt then do; cnt=DQ$READ(conn,@inbuf,buflen,@status); if check$error(0) then wait$time = 0; k=0; if debug then call print(@('back from reading...',crlf)); if cnt=0 then call chk$time; if wait$time=0 then return 0; if debug then call print(@('looping back to read again',crlf)); goto loop; end; return inbuf(k); end getc; putc: procedure (c, conn) public; declare c byte; declare conn token; outbuf(outlen)=c; outlen=outlen+1; if outlen>=buflen then call do$put(conn); end putc; do$put: procedure (conn) public; declare conn token; if outlen>0 then do; call DQ$WRITE(conn,@outbuf,outlen,@status); if check$error(0) then return; outlen=0; end; end do$put; set$end$time: procedure (wait) public; declare wait byte; time$buffer.system$time=0; call DQ$DECODE$TIME(@time$buffer,@status); if check$error(1) then return; wait$time=wait; system$end$time=time$buffer.system$time + double(double(wait)); if debug then do; call print(@('wait_time=',null)); call nout(wait$time); call print(@(' from end_time=',null)); call noutd(system$end$time); call print(@(' and now_time=',null)); call noutd(time$buffer.system$time); call newline; end; end set$end$time; chk$time: procedure public; if debug then call print(@(' enter chk_time...',crlf)); call RQ$SLEEP(10,@status); /* add wait a little? */ if check$error(1) then return; time$buffer.system$time=0; call DQ$DECODE$TIME(@time$buffer,@status); if check$error(1) then return; if time$buffer.system$time>system$end$time then wait$time=0; else wait$time=system$end$time-time$buffer.system$time; if debug then do; call print(@('wait_time=',null)); call nout(wait$time); call print(@(' from end_time=',null)); call noutd(system$end$time); call print(@(' and now_time=',null)); call noutd(time$buffer.system$time); call newline; end; return; end chk$time; spar: procedure (a) public; declare a address; declare b based a byte; b = tochar(send$paclen); /* set up header */ a = a + 1; b = tochar(send$time); a = a + 1; b = tochar(send$padding); a = a + 1; b = ctl(send$padchar); a = a + 1; b = tochar(send$eol); a = a + 1; b = send$quote; end spar; rpar: procedure (addr) public; declare addr address; declare item based addr byte; spsize = unchar(item); /* isn't plm wonderful? */ addr = addr + 1; timeint = unchar(item); addr = addr + 1; numpads = unchar(item); addr = addr + 1; padchar = ctl(item); addr = addr + 1; eol = unchar(item); addr = addr + 1; quote = item; end rpar; bufill: procedure (packet) byte; declare packet address; declare (pp, maxpp) address; declare done byte; declare chr based pp byte; declare i word; done = false; pp = packet; maxpp = pp + spsize - 8; do while not done; if j>=count then do; count = DQ$READ(file$conn,@iobuff,512,@status); if status > 0 then do; call print(@('Error reading file',crlf)); if check$error(0) then return 0; end; if count = 0 then done = true; j=0; end; else do; do i=j to count-1; if ((iobuff(i) and chrmsk) < space) or ((iobuff(i) and chrmsk) = delete) then do; chr = quote; pp = pp + 1; chr = ctl(iobuff(i)); end; else if (iobuff(i) and chrmsk) = quote then do; chr = quote; pp = pp + 1; chr = iobuff(i); end; else chr = iobuff(i); pp = pp + 1; byte$out=byte$out+1; if pp >= maxpp then do; j = i+1; return (pp-packet); end; end; j=count+1; end; end; return (pp - packet); end bufill; /* SPACK: this routine sends a packet of data to the host, it takes */ /* four parameters, the type of packet, message number, packet length */ /* and a pointer to a buffer containing what is to be output. It does */ /* not return a value. */ spack: procedure(type, pknum, length, packet) public; declare (type, pknum, length) byte; declare packet address; declare char based packet byte; declare (i, chksum) byte; if debug then do; call print(@('Sending packet ',null)); call nout(pknum); call newline; end; i = 1; /* do padding */ do while (i <= numpads); call putc(padchar, out$conn); i = i + 1; end; chksum = 0; /* send the packet header */ call putc(send$start, out$conn); /* send packet marker (soh) */ if debug then call co('s'); i = tochar(length + 3); chksum = i; call putc(i, out$conn); /* send character count */ if debug then call co('c'); i = tochar(pknum); chksum = chksum + i; /* add in packet number */ call putc(i, out$conn); /* send packet number */ if debug then call co('n'); chksum = chksum + type; /* add in packet type */ call putc(type, out$conn); /* send the packet type */ if debug then call co(type); /* now send the data */ do i = 1 to length; chksum = chksum + char; call putc(char, out$conn); if debug then call co(char); packet = packet + 1; end; /* check sum generation */ chksum = ((chksum + (chksum and 192) / 64) and 63); chksum = tochar(chksum); call putc(chksum, out$conn); /* send the chksum */ if debug then call co('c'); call putc(eol, out$conn); /* terminate the packet */ if debug then do; call co('e'); call newline; end; call do$put(out$conn); end spack; /* RPACK: this routine receives a packet from the host. It takes three */ /* parameters: the address of where to put the length of the packet, */ /* the address of where to put the packet number and the address of the */ /* buffer to recieve the data. It returns true for a positive reply or */ /* false for a NEGative reply. */ rpack: procedure(length, pknum, packet) byte public; declare (length, pknum, packet, pkptr) address; declare len based length byte; declare num based pknum byte; declare pk based pkptr byte; declare (i, index, chksum, hischksum, type, inchar, msglen) byte; declare buffer(128) byte; if debug then call print(@('rpack | ',null)); inchar = 0; /* wait for a header */ call set$end$time(send$time); do while inchar <> send$start; inchar = getc(in$conn); if wait$time=0 then return 'N'; end; index = 0; call set$end$time(send$time); inchar = getc(in$conn); if wait$time=0 then return 'N'; do while (inchar <> send$eol); buffer(index) = inchar; index = index + 1; inchar = getc(in$conn); if wait$time=0 then return 'N'; end; buffer(index) = null; if debug then do; call print(@('Received packet: [',null)); call print(@buffer); call print(@(']',cr,lf,'Length of message: ',null)); end; msglen = index - 1; if debug then do; call nout(msglen); call newline; call print(@('Length field: ',null)); call nout(buffer(0)); call co('_'); end; len = unchar(buffer(0)-3); if debug then do; call nout(len); call print(@(cr,lf,'Message number: ',null)); call nout(buffer(1)); call co('_'); end; num = unchar(buffer(1)); if debug then do; call nout(num); call print(@(cr,lf,'Type: ',null)); end; type = buffer(2); if debug then do; call co(type); call newline; end; /* debug */ pkptr = packet; chksum = buffer(0) + buffer(1) + buffer(2); i = 3; /* index of first data character */ do while (i < msglen); chksum = (pk := buffer(i)) + chksum; pkptr = pkptr+1; i = i + 1; end; pk = null; /* terminate with null for printing */ pkptr = packet; chksum = (chksum + ((chksum and 192) / 64)) and 63; if debug then do; call print(@('His checksum: ',null)); call nout(buffer(msglen)); call co('_'); end; /* debug */ hischksum = unchar(buffer(msglen)); if debug then do; call nout(hischksum); call print(@(cr,lf,'Our checksum: ',null)); call nout(chksum); call newline; end; /* debug */ if chksum = hischksum then do; if debug then call co('.'); if type='E' then do; if len>0 then call print(@pk); end; return type; end; call print(@('Bad checksum received', crlf)); len=0; return 'E'; end rpack; /* SDATA: this routine sends the data from the buffer area to the host. */ /* It takes no parameters but returns the next state depending on the */ /* type of acknowledgement. */ sdata: procedure byte; declare (num, length, retc) byte; if debug then call print(@('sdata...',crlf)); if tries > maxtry then return 'A'; else tries = tries + 1; call spack('D', msgnum, pklen, .send$packet); retc = rpack(.length, .num, .recv$packet); if (retc = 'N') then return state; if (retc <> 'Y') then return 'A'; /* here when good acknowledgement */ tries = 0; msgnum = (msgnum + 1) mod 64; pklen = bufill(.send$packet); frac$tot=(byte$out*100)/byte$tot; call print(@('output ',null)); call noutd(byte$out); call print(@(' bytes = ',null)); call nout(frac$tot); call print(@('%',cr,null)); if pklen > 0 then return 'D'; else return 'Z'; end sdata; /* SFILE: this routine sends a packet to the host which contains the */ /* filename of the file being sent so that the file can be created at */ /* the host end. It returns a new state depending on the nature of the */ /* the hosts acknowledgement. */ sfile: procedure byte; declare (num, length, retc) byte; declare fnptr address; declare fnindex based fnptr byte; if debug then call print(@('sfile...',crlf)); if tries > maxtry then return 'A'; else tries = tries + 1; if debug then call print(@(cr,lf,'Filename is: ',null)); call prints(@filename); call newline; if debug then do; call print(@(cr,lf,'length is: ',null)); call nout(length); call newline; end; /* debug */ call spack('F', msgnum, filename.len,.filename.name); retc = rpack(.length, .num, .recv$packet); if (retc = 'N') then return state; if (retc <> 'Y') then return 'A'; /* here on valid acknowledgement */ tries = 0; msgnum = (msgnum + 1) mod 64; pklen = bufill(.send$packet); if debug then call nout(pklen); if debug then call newline; if pklen > 0 then return 'D'; else return 'Z'; end sfile; /* SEOF: this routine is used when eof is detected, it closes up and */ /* returns the new state as usual. */ seof: procedure byte; declare (num, length, retc) byte; if debug then call print(@('seof...',crlf)); if tries > maxtry then return 'A'; else tries = tries + 1; call spack('Z', msgnum, 0, .send$packet); retc = rpack(.length, .num, .recv$packet); if (retc = 'N') then return state; if (retc <> 'Y') then return 'A'; /* here on valid acknowledgement */ byte$out=0; tries = 0; /* here is where you open next file if wildcard spec. */ filename.len=0; msgnum = (msgnum + 1) mod 64; if filename.len=0 then return 'B'; else do; call file$open(1); return 'S'; end; end seof; /* SINIT: this routine does initialisations and opens the file to be */ /* send, it returns a new state depending on the outcome of trying to */ /* open the file. */ sinit: procedure byte; declare (len, num, retc) byte; call print(@(cr,lf,'Sending ',null)); if tries > maxtry then return 'A'; else tries = tries + 1; call spar(.send$packet); call spack('S', msgnum, 6, .send$packet); /* send start packet */ retc = rpack(.len, .num, .recv$packet); if (retc = 'N') then return state; if (retc <> 'Y') then return 'A'; /* here on valid acknowledgement */ call rpar(.recv$packet); if eol = 0 then eol = send$eol; if quote = 0 then quote = send$quote; byte$out=0; tries = 0; msgnum = (msgnum + 1) mod 64; return 'F'; end sinit; /* SBREAK: this module breaks the flow of control at the end of a */ /* transmission and allows the send routine to terminate by returning */ /* either a successful or failure condition to the main kermit routine. */ sbreak: procedure byte public; declare (num, length, retc) byte; if debug then call print(@('sbreak...',crlf)); if tries > maxtry then return 'A'; else tries = tries + 1; call spack('B', msgnum, 0, .send$packet); retc = rpack(.length, .num, .recv$packet); if (retc = 'N') then return state; if (retc <> 'Y') then return 'A'; /* we only get here if we received a valid acknowledgement */ tries = 0; msgnum = (msgnum + 1) mod 64; return 'C'; end sbreak; /* serror: this module sends an error packet to abort the transmittion */ serror: procedure byte; declare (num, length, retc) byte; if debug then call print(@('serror...',crlf)); if tries > maxtry then return 'A'; else tries = tries + 1; call spack('B', msgnum, 0, .send$packet); retc = rpack(.length, .num, .recv$packet); if (retc = 'N') then return state; if (retc <> 'Y') then return 'A'; /* we only get here if we received a valid acknowledgement */ tries = 0; msgnum = (msgnum + 1) mod 64; return 'A'; end serror; send$setup: procedure public; msgnum = 0; tries = 0; spsize = send$paclen; timeint = send$time; numpads = send$padding; padchar = send$padchar; eol = send$eol; quote = send$quote; end send$setup; /* SEND: here's the main code for the send command, it's a FSM for */ /* sending files. The main loop calles various routines until it */ /* finishes or an error occurs; this is signified by a true or false */ /* result being returned to the main 'kermit' routine. */ send: procedure byte public; state = 'S'; /* start in Send-Init state */ call send$setup; send_delay=double(send$delay)*100; if co$conn=out$conn then call RQ$SLEEP(send_delay,@status); do while true; if debug then do; call print(@('state : ',null)); call co(state); call newline; end; if state = 'D' then state = sdata; else if state = 'F' then state = sfile; else if state = 'Z' then state = seof; else if state = 'S' then state = sinit; else if state = 'B' then state = sbreak; else if state = 'C' then return true; else if state = 'A' then return false; else if state = 'E' then return false; else return false; end; end send; end send$module; /*---TRANS.P86---*/ /* Transmit routine */ $compact $optimize(3) trans$module: do; $INCLUDE(:INC:LTKSEL.LIT) declare true literally '0FFH'; declare false literally '0'; declare cr literally '0DH'; declare lf literally '0AH'; declare null literally '0'; $INCLUDE(:INC:NSLEEP.EXT) $INCLUDE(:INC:UREAD.EXT) $INCLUDE(:INC:UWRITE.EXT) declare status word external; declare trans_wait word external; declare in$conn token external; declare out$conn token external; declare file$conn token external; declare iobuff(1024) byte external; check$error: procedure (fatal) byte external; declare fatal byte; end check$error; nout: procedure(n) external; declare n word; end nout; do$co: procedure external; end do$co; print: procedure(string) external; declare string pointer; end print; trans: procedure byte public; declare (i,qcr) byte; declare len byte; declare (rec$num,len1) word; rec$num=0; qcr=true; do while true; len=DQ$READ(file$conn,@iobuff,64,@status); if check$error(0) then return false; if len=0 then goto clean$up; len1=256; do i=0 to len-1; iobuff(len1)=iobuff(i); if qcr then do; qcr=false; if iobuff(len1)=lf then len1=len1-1; end; else if iobuff(len1)=cr then qcr=true; len1=len1+1; end; CALL NOUT(LEN1); if len1>256 then call DQ$WRITE(out$conn,@iobuff(256),len1-256,@status); if check$error(0) then return false; rec$num=rec$num+1; call nout(rec$num); call print(@(cr,null)); call RQ$SLEEP(trans_wait,@status); if check$error(0) then return false; len=DQ$READ(in$conn,@iobuff,250,@status); if check$error(0) then return false; end; clean$up: call RQ$SLEEP(trans_wait,@status); if check$error(0) then return false; len=DQ$READ(in$conn,@iobuff,250,@status); if check$error(0) then return false; return true; end trans; end trans$module;