home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / hp1000 / kermit.ftn < prev    next >
Text File  |  2020-01-01  |  172KB  |  4,486 lines

  1. ftn7x,s
  2.       program kermit(6,49)                               ,<890525.1144>
  3.      >File transfer utility
  4.       implicit none
  5.  
  6. !     HP-1000 KERMIT main program
  7. !
  8. !     rev     date        reason..........................................
  9. !     1.98a   23Jul86     First release to CUCCA (supersedes all previous)
  10. !     1.98b   06Aug86     receive/get with rename lost DS info in mask
  11. !                         >>> Released to Interex Detroit swap tape
  12. !     1.98c   09Oct86     Repaired checksum-type parameter-return [recpar]
  13. !     1.99    10Apr87     New CONNECT removes mux lockups; altered CONTROL
  14. !                         and SLEEP routine to go with new CONNECT.  A lot
  15. !                         of reorganization, moving system-dependent code
  16. !                         to KxSUBS (x=A or 6 for RTE-A or RTE-6).  This
  17. !                         makes KERMIT a transportable program!
  18. !     1.99a   14Jul87     Fix in GetMux (KASUBS) for tracking down the DVT
  19. !                         address (near statement label 10).  Several fixes
  20. !                         for compatibility with RTE revision 5.0.
  21. !     1.99b   16Oct87     Fix in ReportFileError to load 3rd segment before
  22. !                         calling SndErr; fix in Connect to (hopefully) let
  23. !                         B- and C-mux terminal-emulation work under RTE-A.
  24. !     1.99c   Jun '88     More fixing to Connect - it is now tested with a
  25. !                         12040D and 12040C, and with a 12792C.  Hopefully
  26. !                         this is the last I need to hear about this!
  27. !                         >>> Released to Interex 2830 CSL tape
  28. !     1.99d   May '89     Fix in RINIT to properly handle timeout.  Now
  29. !                         supports "D" mux on both RTE-A and RTE-6!
  30. !                         >>> Released to CUCCA in Dec. '89
  31. !
  32. !     Permission is granted to any individual or institution to copy
  33. !     or use this program, except for explicitly commerical purpose.
  34. !
  35. !     RTE-6/VM KERMIT was originally implemented by John Lee, of
  36. !     RCA Laboratories 6/29/84.
  37. !
  38. !     Heavily modified by Paul Schumann at E-Systems, Inc. beginning
  39. !     11/09/84 for a "full" implementation of KERMIT, including local-
  40. !     host mode, server operation, and hierarchical file access, on
  41. !     both RTE-6 and RTE-A.
  42. !
  43. !     Installation instructions:
  44. !
  45. !         KERMIT is designed to work on both RTE-6 and RTE-A systems from
  46. !         the C.83 revision (first appearance of CI file space) or later.
  47. !         In order to minimize the installation time, all of the system-
  48. !         dependent code has been moved to K6SUBS.FTN (for RTE-6/VM) or
  49. !         KASUBS.FTN (for RTE-A); the link command-file KERMIT.LOD will
  50. !         select the appropriate routines when you link.
  51. !
  52. !         NOTE: the KERMIT relocatables you received are already set for
  53. !               any system revision from A.84 to 4.1.  If you are actually
  54. !               using a C.83 system, you must edit KERCOM.ftni and change
  55. !               the "SysRev" parameter to any value less than 2440, then
  56. !               recompile KERMIT.  It should not be necessary to recompile
  57. !               KASUBS or K6SUBS.  Failure to do this will result in an
  58. !               undefined external reference to FTRAP.  If you force-load
  59. !               KERMIT, you will be told to make the change just described,
  60. !               and then KERMIT will abort.
  61. !
  62. !         Link KERMIT using the supplied KERMIT.LOD.  Undefined external
  63. !         references (except for FTRAP - see above) are caused by failure
  64. !         to use the correct KERMIT library file (KASUBS or K6SUBS) or be-
  65. !         cause your system is simply too old (before C.83).
  66. !
  67. !         Copy KERMIT.HLP to the /SYSTEM or /KERMIT directory, or if you
  68. !         have no CI space, copy it as "KERMI to any FMGR space.  This
  69. !         file is generated by running the RTE-6 GENIX utility against
  70. !         the KERMIT.TEXT file, which is user-editable.  You need not
  71. !         rebuild KERMIT.HLP if you have not altered KERMIT.TEXT.
  72. !
  73. !                                               Paul Schumann
  74. !                                               E-Systems, Inc.
  75. !                                               PO Box 1056 CBN 148
  76. !                                               Greenville, TX  75401
  77. !                                               (214) 457-5358
  78. !
  79. !     A note on the programming style...
  80. !     I use IMPLICIT NONE in order to protect myself from typos
  81. !     (I am a terrible typist); obviously this means that I must
  82. !     define all symbols before I use them.  I have attempted as
  83. !     much as possible to make the symbol names self-documenting,
  84. !     but I don't LIKE to type either, so I use very short names
  85. !     which I prefixed as follows:
  86. !         fiXx is a variable pertaining to the current file being
  87. !             sent or received
  88. !         dbXx is a variable pertaining to some debugging
  89. !         cmXx is a variable pertaining to the command line
  90. !         fXxx is a logical flag of some type
  91. !
  92. !     I firmly adhere to the concept of "data hiding" as a tool to improve
  93. !     software reliability: a module cannot change a variable if it has no
  94. !     access to it.  I have a number of common blocks with separate include
  95. !     files so that a routine which needs access to control variables but
  96. !     which needs no access to file variables can have exactly that access.
  97. !     Would-be KERMIT modifiers should keep this in mind before they decide
  98. !     to re-combine the common include-files.  (It also improves compiler
  99. !     speed when FTN7X doesn't need to keep up with a lot of unused symbol
  100. !     names!)
  101. !
  102. !     In the interest of saving paper, the complete listings of all common
  103. !     blocks appear only in the block-data routine.  I regret any problems
  104. !     this may cause.
  105. !
  106. !     The following is a definition of ALL variables in /KER/, the
  107. !     main control common block for KERMIT-RTE.  Variables are defined
  108. !     in storage order (for cross-referencing to /KER/ itself).  The
  109. !     numbers in parentheses after a variable name (if any) give the
  110. !     storage allocation in 16-bit words; if absent, assume 1 word.
  111. !
  112. !     SysRev(0)    - the highest system revision-code expected by this
  113. !                    KERMIT version
  114. !     Delay        - # of seconds waited before sending out the first
  115. !                    SINIT packet (only in remote mode).
  116. !     EOL          - End of line delimiter required by other KERMITS.
  117. !     EsChar       - The character used to return back to command mode
  118. !                    from CONNECT.
  119. !     L            - Local TTY channel (to which the user is logged)
  120. !     ImxTry       - Maximum number of retries/packet before giving up on
  121. !                    start-up of a [group] file-transfer
  122. !     MaxTry       - maximum number of retries/packet before giving up on
  123. !                    any packet once transfers are already under way
  124. !     R            - Remote TTY channel (to which user is NEVER logged)
  125. !                    This is also the local channel if KERMIT is in
  126. !                    remote-host mode.
  127. !     p            - a general-purpose byte pointer variable; used by
  128. !                    pPutc and its callers in building a packet
  129. !     rlen         - The data length of the last packet received
  130. !     Seq          - The sequence number of the current packet
  131. !     Class        - The class# to use during CONNECT mode.
  132. !     slen         - The data size of the next packet to send
  133. !     trCb(144)    - Transfer-file control block
  134. !     fePath       - An assigned label.  If a file error of some kind
  135. !                    occurs, execution will continue at (fePath) after
  136. !                    the error is processed.  Only the KERMIT main and
  137. !                    the SERVER subroutine should ever change fePath!
  138. !     sPcnt        - The number of parameters the other KERMIT sent me.
  139. !                    (Some KERMITs don't like to see more parameters than
  140. !                     they sent out, notably an older IBM-PC version...)
  141. !     Parity       - The parity of the remote line:
  142. !                     1 = Even       2 = Mark       3 = None
  143. !                     4 = Odd        5 = Space
  144.  
  145. !     My packet parameters follow:
  146.  
  147. !     PakSiz       - The maximum packet size I want to receive
  148. !     Timeout      - Time (sec) the other KERMIT should wait for me to
  149. !                  - send a packet (0 means wait forever)
  150. !     nPad         - The # of pad characters I require (I require none)
  151. !     EOLch        - The terminator I require at the end of each packet;
  152. !                  - this is hard-coded to 13 (carriage-return)!
  153. !     Quote        - The character I must see before control characters
  154. !     Bit8         - The character I must get if the next data byte is
  155. !                    to have its 8th bit set
  156. !     ChkTyp       - the checksum type I want to receive on packets
  157. !     Repc         - The character which tells me a repeat-count follows
  158. !     capas(2)     - My capabilities bits (I can do time-out; I accept
  159. !                    server commands; I can send/receive attribute packets)
  160. !     Sync         - the character with which my incoming packets start
  161.  
  162. !     My partner's packet parameters follow:
  163.  
  164. !     sPkSiz       - the maximum packet size I am allowed to send
  165. !     sTime        - Time (sec) I will wait for a packet from my partner
  166. !                    (0 means wait forever)
  167. !     sPad         - The number of pad characters my partner needs
  168. !     sPadch       - The character my partner wants for padding
  169. !     sEOL         - My partner's packet-terminator character
  170. !     sQuote       - The character I must put before control characters
  171. !     sBit8        - The character I must send before sending a character
  172. !                  - whose 8th-bit is set
  173. !     sCheck       - My partner's checksum type
  174. !     sRepc        - The character my partner will use before repeat counts
  175. !     sCapas(2)    - My partner's capability bits
  176. !     sSync        - The character I must send as the start of a packet
  177.  
  178. !     Assorted flags follow
  179.  
  180. !     fWarn        - True if file-overwrite warning is desired
  181. !     fIBM         - True if talking to a CMS KERMIT, false otherwise
  182. !                    If true, I must wait for a DC1 (XON or ^Q) before I
  183. !                    can send anything.  Additionally, if I am CONNECTed,
  184. !                    I will locally echo keystrokes.
  185. !     fServ        - True if in server mode, false otherwise
  186. !     fTrans       - True if commands are coming from a transfer-file
  187. !     fBit8        - True if I am doing 8th-bit quoting
  188. !     fRepc        - True if I am doing repeat-count prefixing
  189. !     fPkIO        - True if I am doing packet-I/O
  190. !     fSend        - True if sending a file; false if receiving a file
  191. !                    (valid only during file-transfers)
  192. !     f8OK         - True if 8th-bit quoting is enabled
  193.  
  194. !     String things follow
  195.  
  196. !     HlpNam(32)   - the file-descriptor for KERMIT's help file
  197. !     Packet(50)   - A character array which holds the outgoing packet
  198. !     RecPkt(128)  - A character array which holds the incoming packet
  199. !     pData(48)    - A character array for the data part of a packet
  200. !     state        - The current file-transfer state
  201. !     ErrMsg(36)   - Text of most recent error-message (SndErr takes
  202. !                    the error-packet text from this variable!)
  203. !     Prompt(10)   - The string used as a command-prompt
  204.  
  205. !     Other labeled common areas:
  206. !         /KCMNDS/ forms KERMIT-RTE's vocabulary
  207. !         /KERCMD/ holds the current command-line & parameters
  208. !         /KERCNF/ holds configuration info for the system and for the
  209. !                  two LUs KERMIT is currently using
  210. !         /KERDBG/ holds (self) debugging parameters
  211. !         /KERFIL/ holds control parameters for the file being sent or
  212. !                  received currently.
  213. !         /KERSTA/ holds file-transfer statistical information
  214.  
  215. !     WARNING -- KERMIT uses "unusual" techniques in order to keep a large
  216. !         program comfortably within the address space, while maintaining
  217. !         the functionality.  These are:
  218. !         1)  'ASSIGN nn to fePath' appears in KERMIT's main program only.
  219. !             "fePath" is a variable in KERCOM which is used only by the
  220. !             file-error reporting routine, ReportFileError, to return to
  221. !             the main quickly without propagating an error-code through
  222. !             the subroutine call-chain.  WARNING: this usage alone makes
  223. !             KERMIT unfit for Code and Data Separation (CDS), although
  224. !             there is a CDS-compatible method which performs the same job.
  225. !         2)  KERMIT is segmented in an unusual way: all modules in the
  226. !             segments (except for the "segment header") are subroutines or
  227. !             functions that are called FROM SOME PART OF THE MAIN!  This
  228. !             method was used to reduce KERMIT (1.97 and previous revisions)
  229. !             from 31 pages (under RTE-A) to 24 before the RUn command was
  230. !             added.  This "feature" makes KERMIT unfit for LOADR; you must
  231. !             use LINK.  (Actually, there is a method by which LOADR can be
  232. !             made to load this KERMIT, but it involves splitting the object
  233. !             files at segment boundaries -- it isn't worth the trouble!)
  234. !
  235. !         If you find bugs in KERMIT, please let me know!  I can advise you
  236. !         in the most expedient wat to fix them, as well as fix them for
  237. !         other KERMIT users.  If you contemplate extending the capabilities
  238. !         of this KERMIT, be sure you understand the segmenting method.  A
  239. !         few guidelines are:
  240. !             a)  If it is already in the main, don't move it to any of
  241. !                 the segments, or you'll probably lose return addresses!
  242. !             b)  If it parses an interactive command, at least some of it
  243. !                 must be in K1CMD's (command processing) segment.
  244. !             c)  If it deals ONLY with communicating to the other end
  245. !                 (file-transfers or server), it >>probably<< belongs in
  246. !                 K3XFR's (packet-transfer) segment.
  247. !             d)  K2MSK's segment may appear the smallest in terms of the
  248. !                 number of source lines, but all of the file-masking code
  249. !                 loads there.  It is still the smallest segment, but it's
  250. !                 unlikely that any more useful code could be put there.
  251. !             e)  If it may be called from 2 or more segments, and all
  252. !                 calls it makes are to the system or other modules in
  253. !                 the main only, it >>probably<< belongs in the main.
  254. !
  255. !     I apologize in advance for any problems this may cause the reader.
  256.  
  257.       include kercom.ftni,NOLIST
  258.       include kercmd.ftni,NOLIST
  259.       integer*2 loglu,junk
  260.       logical*2 succeed,send,receive
  261.  
  262.       L = loglu(junk)                     !Get user's I/O channel
  263.       do junk = 3,1,-1                    !Insure all segments load
  264.           call LoadSeg(junk)              !...ending with command-processor
  265.       end do
  266.  
  267. !     K1CMD performs initialization code on 1st entry only.
  268.  
  269.       assign 10 to fePath                 !Set File-error path
  270.  
  271. !     The following infinite loop is terminated by
  272. !         a)  An exit-type command if interactive
  273. !         b)  A finish-type command if serving
  274.  
  275.  10   if ( fServ ) then                   !Serving?
  276.           call LoadSeg(3) $ call Server   !Yes - Server never returns
  277.       else
  278.           call LoadSeg(1) $ Call Command  !No - do interactive commands
  279.       endif
  280.  
  281. !     A command requiring packet-I/O is ready to execute
  282.  
  283.       call LoadSeg(3) $ call PakIO        !Prepare for packet-I/O
  284.       if (cmTk .eq. 'BYE') then
  285.           call Bye
  286.       else if (cmTk .eq. 'FINISH') then
  287.           call Finish
  288.       else if (cmTk .eq. 'GET') then
  289.           call Get
  290.       else if (cmTk .eq. 'RECEIVE') then
  291.           succeed = receive('R')
  292.           call success(succeed)
  293.       else if (cmTk .eq. 'SEND') then
  294.           if (L .eq. R) call sleep(delay)
  295.           succeed = send()
  296.           call success(succeed)
  297.       endif
  298.       cmCh = ' ' $ call NrmIO $ goto 10   !Get something else to do
  299.  
  300.       end
  301.  
  302.       Subroutine LoadSeg(SegNum)                         ,<890525.1144>
  303.      >Overlay loader
  304.       implicit none
  305.  
  306.       include kercom.ftni,NOLIST
  307.       integer*2 SegNum,err,segn(3)
  308.       character*6 cSegNam,SegNames(3)
  309.       equivalence (cSegNam,segn)
  310.       data SegNames /'K1CMD ','K2MSK ','K3XFR '/
  311.  
  312.       if (segnum .eq. seg) return         !Segment is already loaded
  313.       cSegNam = SegNames(SegNum)
  314.       call SegLd(segn,err)                !Try to load the segment
  315.       if (err .ne. 0) then                !Did it load (and return)?
  316.           call tpI2('Segment-Loader error _',err,0)
  317.           call tpCh(' on -',cSegNam)
  318.           stop 'aborting with segment-load problems'
  319.       endif
  320.  
  321.       return
  322.       end
  323.  
  324.       Subroutine tPrint                                  ,<890525.1144>
  325.      >Term-print mini-formatter
  326.       implicit none
  327.  
  328.       include kercom.ftni,NOLIST          !To define "L"
  329.  
  330. !     Define the names of all entry parameters
  331.  
  332.       integer*2 i2Var                     !Integer*2 variable to print
  333.       integer*4 i4Var                     !Integer*4 variable to print
  334.       integer*2 iflen                     !length of the integer field
  335.       character*(*) chFmt                 !"format" string to print
  336.       character*(*) chVar                 !character variable to print
  337.  
  338.       integer*2 tp,ioBuf(129),TrimLen,xl(2),cw
  339.       logical*2 fReady                    !True when string ready to print
  340.       integer*4 w4,mul
  341.       character*20 IntToDecimal,DintToDecimal,chWk,jRight
  342.       character chBuf*258
  343.       equivalence (ioBuf,chBuf)
  344.       data tp /1/                         !IO-buffer starts at byte# 1
  345.       data cw /0/                         !No control bits on the LU
  346.  
  347. c     Formatting rules:
  348. c         If a "format" ends with an underscore ("_") then the string
  349. c             being built will not be printed on this call, but the
  350. c             format string will be put in the I/O buffer without it.
  351. c         If a "format" ends with a dash ("-") then the string being
  352. c             built will be printed without it.  This allows trailing
  353. c             blanks to be put in the format which would otherwise be
  354. c             deleted.
  355. c         The field length parameter is given as the number of digits
  356. c             allowed for the formatted number if right-justification
  357. c             is desired.  If the field length is positive, blanks are
  358. c             padded on the left as needed; if negative, zeroes are
  359. c             padded on the left.   If the field length is zero, the
  360. c             number will be printed left-justified.
  361. c         If the number being formatted occupies more characters in the
  362. c             string than the field-length, the number will display as
  363. c             (iflen) stars (just like the formatter!).
  364. c         A maximum of 257 characters may ever be printed at one time.
  365.  
  366.       entry tpFm(chFmt)                   !Append a format only
  367.       call CopyMore(chFmt,chBuf,tp,fReady)
  368.       goto 10
  369.  
  370.       entry tpI2(chFmt,i2Var,ifLen)       !Append format and I*2 number
  371.       call CopyMore(chFmt,chBuf,tp,fReady)
  372.       chWk = jRight(IntToDecimal(i2var),iflen)
  373.       chBuf(tp:) = chWk                   !Copy in the number
  374.       tp = tp + TrimLen(chWk)             !Adjust the pointer
  375.       goto 10
  376.  
  377.       entry tpI4(chFmt,i4Var,ifLen)       !Append format and I*4 number
  378.       call CopyMore(chFmt,chBuf,tp,fReady)
  379.       chWk = jRight(DintToDecimal(i4Var),iflen)
  380.       chBuf(tp:) = chWk
  381.       tp = tp + TrimLen(chWk)
  382.       goto 10
  383.  
  384.       entry tpCh(chFmt,chVar,ifLen)       !Append format and string
  385.       call CopyMore(chFmt,chBuf,tp,fReady)
  386.       chBuf(tp:) = chVar
  387.       if (ifLen .gt. 0) then              !Was a field-size given
  388.           tp = tp + ifLen                 !Yes - "truncate" at that size
  389.       else
  390.           tp = tp + TrimLen(chVar)        !No - use all but trailing blanks
  391.       endif
  392.  
  393.  10   tp = min(tp,258)                    !Don't overflow the string
  394.       if (fReady) then                    !If string is ready to print...
  395.           xl = L                          !Set LU in XLUEX control words
  396.           call xluex(2,xl,iobuf,1-tp)     !...print it
  397.           tp = 1                          !...and reset the pointer
  398.       endif
  399.  
  400.       return
  401.       end
  402.  
  403.       subroutine CopyMore(frm,dest,tp,fdone)             ,<890525.1144>
  404.      >do "formats"
  405.       implicit none
  406.  
  407.       character*(*) frm,dest
  408.       integer*2 tp
  409.       logical*2 fdone
  410.       integer*2 i,TrimLen
  411.  
  412.       i = TrimLen(frm)                    !Where does the format end?
  413.  
  414.       if (frm(i:i) .eq. '_') then         !Are we finishing the buffer?
  415.           fdone = .false.                 !No - flag it to caller
  416.           i = i - 1                       !Don't copy continue flag to buf
  417.       else
  418.           fdone = .true.
  419.           if (frm(i:i) .eq. '-') i=i-1    !Remove trailing-blanks flag
  420.       endif
  421.  
  422.       if (i .gt. 0) then                  !Anything to copy?
  423.           dest(tp:) = frm(:i)             !Copy the correct part
  424.           tp = tp + i                     !adjust the pointer
  425.           tp = min(tp,258)
  426.       endif
  427.  
  428.       return
  429.       end
  430.  
  431.       character*20 function jRight(chNum,flen)           ,<890525.1144>
  432.      >do "Ix" formats
  433.       implicit none
  434.  
  435.       character*(*) chNum
  436.       integer*2 flen
  437.       integer*2 i,ilen,TrimLen
  438.       character*20 stars,blanks,zeroes
  439.       data stars   /'********************'/
  440.       data blanks  /'                    '/
  441.       data zeroes  /'00000000000000000000'/
  442.  
  443.       ilen = iabs(flen)
  444.       if (ilen .gt. 20) then
  445.           call quit
  446.           stop 'I-field length > 20 (jRight)'
  447.       endif
  448.       i = TrimLen(chNum)                  !How big is the number
  449.       if (ilen .ne. 0) then               !Was a field-length given?
  450.           if (i .gt. ilen) then           !Yes - will the number fit?
  451.               jRight = stars(:ilen)       !No - replace number with stars
  452.           else if (i .lt. ilen) then
  453.               i = ilen - i                !fits ok; get # of fill bytes
  454.               if (flen .lt. 0) then       !Doing zero or blank fill?
  455.                   jRight = zeroes(:i) // chNum
  456.               else
  457.                   jRight = blanks(:i) // chNum
  458.               endif
  459.           else
  460.               jRight = chNum              !exact fit
  461.           endif
  462.       else
  463.           jRight = chNum                  !Else no formatting requested
  464.       endif
  465.  
  466.       return
  467.       end
  468.  
  469.       subroutine FtnTrap(abreg,preg)                     ,<890525.1144>
  470.      >Trap FORTRAN errors
  471.       implicit none
  472.  
  473.       include kercom.ftni,NOLIST
  474.       include kerdbg.ftni,NOLIST
  475.  
  476.       integer*2 abreg(2),preg(2),abcod(3)
  477.       character ecode*6, addr*8, IntToDecimal*6, IntToOctal*6
  478.       equivalence (abcod,ecode)
  479.  
  480. !     If KERMIT incurs a FORTRAN error, it will most likely be a type-2
  481. !     (always fatal) error.  The point of this routine is to insure the
  482. !     restoration of all MUX configurations and file-closure should the
  483. !     "impossible" happen.  In fact, the most likely error will be from
  484. !     a string routine.  On completion of it's task, FtnTrap allows the
  485. !     standard "runtime error" termination to occur.
  486.  
  487.       addr = IntToOctal(preg)//'/'//char(seg+48) !Format error addr/seg#
  488.  
  489.       if (abreg .lt. 20000b) then         !Group-2 error?
  490.           ecode = IntToDecimal(abreg)     !Yes - format the number
  491.       else
  492.           abcod = abreg                   !Copy Group-1/-3 error to the
  493.           abcod(2) = abreg(2)             !message
  494.           abcod(3) = 2h                   !Clear the end of the message
  495.       endif
  496.  
  497.       ErrMsg = 'Runtime error ' // ecode // ' @' // addr
  498.       call kdebug(all,ErrMsg,' ')         !Try to log the error
  499.       call LoadSeg(3) $ Call SndErr       !Do error-packet if in transfer
  500.  
  501.       call quit                           !Clean myself up
  502.       return                              !allow me to abort
  503.       end
  504.  
  505.       subroutine kdebug(type,header,info)                ,<890525.1144>
  506.      >Do KERMIT debug logging
  507.       implicit none
  508.  
  509.       include kerdbg.ftni,NOLIST
  510.       integer*2     type
  511.       character*(*) header,info
  512.       integer*2     LogBf,TrimLen,err
  513.       character*150 LogCh
  514.       equivalence   (LogCh,LogBf)
  515.  
  516. c     This routine performs the debug logging requested by KERMIT's
  517. c     user.  If debug logging of (type=STATES, PACKET, or ALL) is
  518. c     enabled and logging is not suspended, then (header) and (info)
  519. c     are concatenated into a log record and written to the log file.
  520.  
  521.       if (dbLv .lt. 1) return             !No debugging is active
  522.       if (iand(type,dbLv) .ne. 0) then    !Logging this type of stuff?
  523.           LogCh = ' ' // header // info   !Yes - form the record
  524.           call FmpWrite(dbCb,err,LogBf,TrimLen(LogCh))   !...& write it
  525.           call FmpPost(dbCb,err)
  526.       endif                               !IGNORE file-write errors!
  527.  
  528.       return
  529.       end
  530.  
  531.       subroutine quit                                    ,<890525.1144>
  532.      >Terminate KERMIT cleanly
  533.       implicit none
  534.  
  535.       include kerfil.ftni,NOLIST          !Defines fiCB and maCB
  536.       include kercom.ftni,NOLIST          !Defines trCB, R, and L
  537.       include kercnf.ftni,NOLIST          !Defines fRmx and fLmx
  538.       include kerdbg.ftni,NOLIST          !Defines dbCB
  539.       integer*2 junk
  540.  
  541.       call FmpClose(fiCB,junk)
  542.       call FmpClose(dbCB,junk)
  543.       call FmpClose(trCB,junk)
  544.       call FmpEndMask(maCb)
  545.  
  546.       !The next line is an attempt to prevent both ends of a CPU-CPU
  547.       !link from trying (and always failing) to log each other on.
  548.       !Hopefully, 5 seconds is enough time for logout- or program-
  549.       !termination activity to stop before this side's interrupts
  550.       !get re-enabled.
  551.  
  552.       if (R .ne. L) then
  553.           call sleep(500)
  554.           call restore(L)                 !Restore local configuration
  555.           call enable(L,fLmx)             !...and interrupt-scheduling
  556.       endif
  557.  
  558.       call restore(R)                     !Restore remote configuration
  559.       call enable(R,fRmx)                 !Restore remote interrupt-sched
  560.  
  561.       call lurq(100000b)                  !Release any locks I've done
  562.       return
  563.  
  564.       end
  565.  
  566.       real*4 function control(lu,fcn,param)              ,<890525.1144>
  567.      >Perform control calls
  568.       implicit none
  569.  
  570.       real*4 rstat
  571.       integer*2 lu,fcn,param,xl(2),cw
  572.       equivalence (xl(2),cw),(xl,rstat)
  573.  
  574. !     While this routine is >never< actually called as a function, we define
  575. !     it as such so that ABReg will work to return the status of the control
  576. !     request to the caller (as used by connect).
  577.  
  578.       xl = lu
  579.       cw = fcn
  580.       call xluex(3,xl,param)              !Perform the control function
  581.       call abreg(xl,cw)                   !get the status of the request
  582.       control = rstat                     !Return it to the caller
  583.  
  584.       return
  585.       end
  586.  
  587.       subroutine ReportFileError(err,nam)                ,<890525.1144>
  588.      >Report file errors
  589.       implicit none
  590.  
  591. c     Since it is the nature of this routine to never return to its
  592. c     caller, we will ALWAYS close the current file (being sent or
  593. c     received) as well as terminating any masked-/indirect-search
  594. c     operation in progress
  595.  
  596.       include kercom.ftni,NOLIST
  597.       include kercmd.ftni,NOLIST
  598.       include kerdbg.ftni,NOLIST
  599.       include kerfil.ftni,NOLIST
  600.       integer*2 err,j,TrimLen
  601.       character*(*) nam
  602.  
  603.       cmCh = ' '                          !Clear the current command
  604.       call FmpError(err,ErrMsg)           !Decode the error-number
  605.       j = TrimLen(ErrMsg) + 1             !Find the end of the text
  606.       if (TrimLen(nam) .gt. 0) then
  607.           ErrMsg(j:) = ': ' // nam        !Tack on the file-name
  608.       endif
  609.  
  610.       call FmpClose(fiCb,err)             !Shut down file/mask operations
  611.       call FmpEndMask(maCb)
  612.  
  613.       call kdebug(all,ErrMsg,' ')         !Log the error-message (debug)
  614.       call LoadSeg(3) $ Call SndErr       !If in packet-I/O, do error-pkt
  615.  
  616.       if (R.eq.L .and. .not. fServ) then
  617.           call NrmIO                      !Restore "local" in remote-host
  618.       endif
  619.  
  620.       goto fePath                         !Return to KERMIT MAIN PROGRAM
  621.  
  622.       end
  623.  
  624.       subroutine Server                                  ,<890525.1144>
  625.      >Be a KERMIT Server
  626.       implicit none
  627.  
  628. c     The only way out of this, once begun, is to receive a FINISH or
  629. c     BYE command from the other KERMIT.
  630.  
  631.       include kercom.ftni,NOLIST
  632.       include kercnf.ftni,NOLIST          !Defines fRmx
  633.       include kerdbg.ftni,NOLIST
  634.       include kerfil.ftni,NOLIST
  635.       integer*2 len,num
  636.       logical*2 receive,send
  637.       character*1 pt,RecPack
  638.  
  639.  10   call Set_Timeout(R,9000,fRmx)       !Do 90-second server timeout
  640.       sCheck = 1                          !Server commands use 1-byte checks
  641.       call LoadSeg(3)                     !Insure file-xfr subs are callable
  642.       call PakIO                          !Set flags for packet-I/O
  643.  
  644.       pt = RecPack(len,num)               !Get a command packet
  645.  
  646.       if (pt.eq.'S' .or. pt.eq.'I') then  !Send file or initialize?
  647.           seq = num                       !Yes - make seq# agree
  648.           fSend = .false.                 !Do param-stuff like a receive
  649.           call RecPar(len)                !Get partner's parameters
  650.           if (fBnry .and. Parity.ne.3 .and. .not.fBit8) then
  651.               ErrMsg = 'Can''t receive binary file (parity problem)'
  652.               call SndErr
  653.               fBnry = .false.
  654.               goto 10
  655.           endif
  656.           if (pt .eq. 'I') call doIpacket !Change my params to remote's vals
  657.           call SndPar('Y',seq)            !...and send mine
  658.           if (pt .eq. 'S') then           !Actually send a file?
  659.               sCheck = NewChk             !OK to change checksum type now
  660.               first = .true.              !Allow LEGALIZE to process RMASK
  661.               mask = rmask                !Set the receive mask
  662.               seq = mod(seq+1,64)
  663.               if (receive('F')) then
  664.                   call kdebug(all,'Server receive completed',' ')
  665.               else
  666.                   call kdebug(all,'Server receive failed',' ')
  667.               endif
  668.           endif
  669.       else if (pt .eq. 'R') then          !Send file(s)?
  670.           first = .true.                  !Prepare to do file masking
  671.           fiNm = ' '                      !Clear old file name
  672.           mask = RecPkt(5:len+4)          !Get the file mask
  673.           if (send()) then
  674.               call kdebug(all,'Server send completed',' ')
  675.           else
  676.               call kdebug(all,'Server send failed',' ')
  677.           endif
  678.  
  679.       else if (pt .eq. 'G') then          !Generic command?
  680.           pt = RecPkt(5:5)                !Get the data field
  681.           if (pt .eq. 'F') then           !Finish?
  682.               call SndPack('Y',num,0)     !Yes - acknowledge it
  683.               call quit                   !Turn off everything
  684.               call exec(6)                !...and stop
  685.  
  686.           else if (pt .eq. 'L') then      !Bye (logout)?
  687.               call SndPack('Y',num,0)     !Acknowledge it
  688.               call quit                   !Turn everything off
  689.               call Logoff3                !and log off the session
  690.  
  691.           else                            !Other generic packet type?
  692.               ErrMsg = 'Unknown generic packet type: ' // pt
  693.               call kdebug(all,ErrMsg,' ')
  694.               call SndErr
  695.           endif
  696.  
  697.       else if (pt .eq. 'T') then          !Time-out?
  698.           seq = 0                         !Reset the sequence number
  699.           call SndPack('N',seq,0)         !Guard against lost packets
  700.           goto 10                         !Ignore it
  701.  
  702.       else if (pt .ne. 'N') then          !Something other than NAK?
  703.           ErrMsg = 'Unknown Server packet type: ' // pt
  704.           call kdebug(all,ErrMsg,' ')
  705.           call SndErr
  706.       endif
  707.  
  708.       goto 10
  709.  
  710.       end
  711.  
  712.       logical*2 function Send()                          ,<890525.1144>
  713.      >Send-state switch
  714.       implicit none
  715.  
  716.       include kercom.ftni,NOLIST
  717.       include kerdbg.ftni,NOLIST
  718.       include kerfil.ftni,NOLIST
  719.       include kersta.ftni,NOLIST
  720.       integer*2 retry
  721.       character*1 sdata,sfile,seof,sinit,sbreak
  722.       character*3 c_r
  723.  
  724.       state = 'S'                         !Set initial state
  725.       c_r = char(13) // ' _'
  726.       fSend = .true.                      !Set Send/Receive flag for send
  727.       send = .false.                      !Clear the success flag
  728.       retry = 0                           !Reset the retry-counter
  729.       call LoadSeg(2) $ call NextFile     !Get 1st file to send
  730.       if (fiNm .eq. ' ') return           !Nothing to send
  731.       call LoadSeg(3) $ call StartStats   !Prepare for packet-I/O
  732.  
  733.  10   if (state .eq. 'D') then            !Send data?
  734.           state = sdata(retry)
  735.       else if (state .eq. 'F') then       !Send file header?
  736.           state = sfile(retry)
  737.       else if (state .eq. 'Z') then       !Send EOF?
  738.           state = seof(retry)             !Yes - send the packet
  739.           if (state .eq. '@') then        !Ready for next file?
  740.               call LoadSeg(2)             !Get the file-masking routines
  741.               call NextFile               !Get next file to send
  742.               call LoadSeg(3)             !Restore packet-I/O capability
  743.               if (fiNm .eq. ' ') then     !Is there one?
  744.                   state = 'B'             !No - break the connection
  745.               else
  746.                   state = 'F'             !Yes - do a file-header
  747.               endif
  748.           endif
  749.       else if (state .eq. 'S') then       !Send initial packet?
  750.           state = sinit(retry)
  751.       else if (state .eq. 'B') then       !Send final packet?
  752.           state = sbreak(retry)
  753.       else if (state .eq. 'C') then       !Last file sent?
  754.           call endstats                   !Turn off statistics logging
  755.           send = .true.
  756.           return
  757.       else if (state .eq. 'E') then       !Did we receive an error packet?
  758.           call endstats
  759.           if (.not. fServ) call tpFm(ErrMsg)
  760.           return
  761.       else if (state .eq. '!') then       !Did we get an error?
  762.           call endstats
  763.           call SndErr
  764.           return
  765.       else                                !Unknown packet?
  766.           call endstats
  767.           ErrMsg = 'Send-state error; state = ' // state
  768.           call kdebug(states,ErrMsg,' ')
  769.           if (R .ne. L) then
  770.               call tpFm(ErrMsg)
  771.           endif
  772.           call SndErr
  773.           return
  774.       endif
  775.  
  776.       call kdebug(states,'TxState: ',state)
  777.       if (retry .ne. 0) rtry = rtry + 1
  778.       if (R .ne. L) then
  779.           call tpI2(c_r,spak,6)
  780.           call tpI2('/_',rtry,-3)
  781.           call tpFm(' _-')
  782.       endif
  783.  
  784.       goto 10
  785.       end
  786.  
  787.       subroutine sleep(csec)                             ,<890525.1144>
  788.      >Delay for N centiseconds
  789.       implicit none
  790.  
  791.       integer*2 csec,i
  792.  
  793.       if (csec .gt. 0) then               !Need negative units to wait
  794.           i = -csec
  795.       else
  796.           i = -2                          !Always wait at least 2 units
  797.       endif
  798.  
  799.       call exec(12,0,1,0,i)
  800. c                ^ ^ ^ ^ ^
  801. c                ! ! ! ! +----> number of units to delay
  802. c                ! ! ! +------> repeat this how many times?" (none = 0)
  803. c                ! ! +--------> Units are centiseconds
  804. c                ! +----------> Suspend myself (not some other task)
  805. c                +------------> Executive request code to suspend
  806. c     In order to read the above diagram, read upwards:
  807. c         "I want the executive to suspend me once for (csec) centiseconds,
  808. c          and then I will do something else."
  809.  
  810.       return
  811.       end
  812.  
  813.       block data main_common                             ,<890525.1144>
  814.      >KERMIT's labeled common areas
  815.       implicit none
  816.  
  817.       include kcmnds.ftni
  818.       include kconcw.ftni
  819.       include kercmd.ftni
  820.       include kercnf.ftni
  821.       include kercom.ftni
  822.       include kerdbg.ftni
  823.       include kerfil.ftni
  824.       include kersta.ftni
  825.  
  826.       data Lsyu       /0/                 ! Insure 1st-time stuff is done
  827.  
  828.       data seg        /0/                 ! Insure 1st LoadSeg call works
  829.       data delay      /1500/              ! 15 sec from SEND to 1st pkt
  830.       data EsChar     /29/                ! CTRL-] returns to local KERMIT
  831.       data MaxTry     /5/                 ! Normal retry-limit is 5
  832.       data ImxTry     /15/                ! Initial retry-limit is 15
  833.       data R          /0/                 ! Remote LU is unknown at startup
  834.       data seq        /0/                 ! Sequence numbers starts at 0
  835.       data Class      /0/                 ! We get a class# as needed
  836.       data sPcnt      /9/                 ! I only have 9 parameters to send
  837.       data Parity     /0/                 ! 'Not set yet'
  838.  
  839. ! My packet parameters follow
  840.       data PakSiz     /94/                ! Current packet size
  841.       data Timeout    /0/                 ! (filled in by GetPakTime)
  842.       data nPad       /0/                 ! I require no padding, but...
  843.       data Padch      /0/                 !  ...use nulls if you must pad
  844.       data EOLch      /13/                ! I require CR as a terminator
  845.       data Quote      /35/                ! I want "#" before controls
  846.       data Bit8       /38/                ! I want "&" before 8th-bit set
  847.       data ChkTyp     /1/                 ! I do type-1 checksums
  848.       data Repc       /126/               ! Repeat-count prefix is "~"
  849.       data Sync       /1/                 ! CTRL-A starts my packets
  850.  
  851. ! My partner's defaults are reset by subroutine RecPar to:
  852.       data sPkSiz     /80/                ! Packet size
  853.       data sTime      /30/                ! Timeout (never wait past 30 sec)
  854.       data sPad       /0/                 ! I'll never send padding
  855.       data sPadch     /0/                 ! (If I pad, I'll use nulls)
  856.       data sEOL       /13/                ! <CR> is end-of-packet
  857.       data sQuote     /35/                ! "#" precedes control-characters
  858.       data sBit8      /32/                ! No 8th-bit quoting assumed
  859.       data sCheck     /1/                 ! 1-byte checksums used
  860.       data sRepc      /32/                ! No repeat-counts assumed
  861.       data sSync      /1/                 ! CTRL-A starts your packets
  862.  
  863. ! Utility defaults follow
  864.       data fWarn      /.true./            ! I will warn of file overwrites
  865.       data fIBM       /.false./           ! I am not in IBM mode
  866.       data fServ      /.false./           ! I am not in server mode
  867.       data fEcho      /.false./           ! I am not echoing transfer-file
  868.       data fTrans     /.false./           ! I am not using a transfer-file
  869.       data f8OK       /.true./            ! I will allow 8-th bit quoting
  870.       data HlpNam     /' '/               ! I don't know the help file name
  871.  
  872.       data state      /'C'/               ! File-transfer is C(omplete)
  873.       data Prompt     /'Kermit-RTE>'/     ! Command-line prompt
  874.  
  875. ! File defaults follow
  876.       data fBnry      /.false./           ! Assume ASCII transfers
  877.       data Mask       /' '/               ! Set default (directory) mask
  878.       data Rmask      /' '/               ! Clear the server-receive mask
  879.       data fiNm       /' '/               ! Clear the file-name storage
  880.  
  881. ! Debugging defaults follow
  882.       data dbNm       /' '/               ! No debugging file opened
  883.       data dbLv       /0/                 ! No debugging in progress
  884.  
  885. ! Status stuff follows
  886.       data tspak      /0/                 ! No packets sent yet     (total)
  887.       data trpak      /0/                 ! No packets received yet (total)
  888.       data trtry      /0/                 ! No retries yet          (total)
  889.       data spak       /0/                 ! No packets sent yet     (current)
  890.       data rpak       /0/                 ! No packets received yet (current)
  891.       data rtry       /0/                 ! No retries yet          (current)
  892.       data sbytes     /0/                 ! No bytes sent
  893.       data rbytes     /0/                 ! No bytes received
  894.       data sovrhd     /0/                 ! No overhead bytes sent
  895.       data rovrhd     /0/                 ! No overhead bytes received
  896.  
  897. !     WARNING: The order of the pvalues must match the order of the parity
  898. !              commands themselves in /KCMNDS/
  899. !     Parity bits =  EVEN,   MARK,    NONE,    ODD,  SPACE
  900.       data pvalu / 41400b, 41000b, 140000b, 40400b, 40000b,
  901.      >             41400b, 40400b,      0b, 41000b, 40000b/
  902.  
  903. ! Command constants follow
  904.       data commands/
  905.      >    'BYE',    'CONNECT', 'EXIT',    'FINISH', 'GET',
  906.      >    'HELP',   'QUIT',    'RECEIVE', 'RUN',    'SEND',
  907.      >    'SERVER', 'SET',     'SHOW',    'STATUS', 'TRANSFER'/
  908.       data setparms/
  909.      >    'BINARY', 'BQUOTE', 'CHECK',  'DEBUG',  'DELAY',  'ESCAPE',
  910.      >    'IBM',    'LINE',   'PACKET', 'PARITY', 'PROMPT', 'QUOTE',
  911.      >    'REPEAT', 'RETRY',  'RMASK',  'SYNC',   'WARNING'/
  912.       data parits  / 'EVEN', 'MARK', 'NONE', 'ODD',     'SPACE'/
  913.       data debugs  / 'ALL',  'FILE', 'OFF',  'PACKETS', 'STATES'/
  914.  
  915.       end
  916.       program K1CMD(5)                                   ,<890525.1144>
  917.      >KERMIT command processors
  918.       implicit none
  919.  
  920.       include kercom.ftni,NOLIST
  921.       include kercmd.ftni,NOLIST
  922.       include kercnf.ftni,NOLIST
  923.  
  924. $alias /datc/ = '$DATC', NoAllocate
  925. $alias xla    = '.XLA', direct
  926.  
  927.       integer*2 datc,SyRv,xla
  928.       common /datc/ datc
  929.  
  930.       seg = 1
  931.       if (Lsyu .eq. 0) then               !Need to initialize?
  932.           call GetMux(L,LocCnf)           !Get the local configuration
  933.           SyRv = xla(datc)                !Get the system date-code
  934.  
  935.           call tpCh('HP-1000 RTE-KERMIT Version 1.99d <890525.1144>'
  936.      >                                                ,char(10),0)
  937.           call tpCh(' KERMIT-RTE requires EOL=13!',char(10),0)
  938.  
  939.           call HostMode                   !Check and/or change host mode
  940.           fServ = .false.                 !Can't be serving on startup
  941.           cmCh = ' '                      !Clear the command-line
  942.           call getst(cmIn,-65,cmLn)       !Get a possible run-string
  943.  
  944.           if (SyRv .gt. 5010) then        !Newer than I know about?
  945.               call tpFm('BEWARE - KERMIT has not been tested_')
  946.               call tpFm(' under this system revision!')
  947.           endif
  948.  
  949.           call SetTrap(SyRv)              !Set FORTRAN-traps as needed
  950.       endif
  951.  
  952.       call SegRt
  953.  
  954.       end
  955.  
  956.       subroutine Command                                 ,<890525.1144>
  957.      >Do user commands
  958.       implicit none
  959.  
  960.       include kercom.ftni,NOLIST
  961.       include kercmd.ftni,NOLIST
  962.       include kcmnds.ftni,NOLIST
  963.       include kerdbg.ftni,NOLIST
  964.       integer*2 i,err,xl(2),cw,FmpOpen,FmpRead,TrimLen,Match
  965.       logical*2 MustBeLocal
  966.       character*1 cmC1(80)
  967.       equivalence (xl(2),cw),(cmC1,cmLn)
  968.       data cw /400b/                      !Set Echo on terminal LU
  969.  
  970.       xl = L                              !Put LU into control-word
  971.  
  972.  10   if (fTrans) then                    !Getting transfer-file commands?
  973.           cmCh = ' '                      !Yes - clear the command-line
  974.           cmLn=FmpRead(trCb,err,cmIn,65)  !Yes - get one
  975.           if (err.lt.0 .or. cmLn.lt.0) then  !On EOF or error...
  976.               call FmpClose(trCb,fTrans)     !(don't destroy read-error)
  977.               fTrans = .false.               !Turn off transfer-file flag
  978.           endif
  979.           if (err.lt.0) call ReportFileError(err,'<transfer-file>')
  980.       endif
  981.  
  982. !     By first going to the transfer-file for a command and allowing zero-
  983. !     length commands, I can perform a sort of 'return to the console'
  984. !     operation FOR ONE COMMAND ONLY per blank line in in the transfer-
  985. !     file.  If you find yourself at a console prompt unexpectedly, you
  986. !     can make KERMIT go back to the transfer-file (without processing a
  987. !     dummy command) by entering one or more commas only.
  988.  
  989.       cmLn = TrimLen(cmCh)
  990.       if (cmLn .lt. 1) then
  991.           fPkIO = .false.                 !Can't be in packet-I/O here
  992.  20       call tpCh(Prompt,' _',0)        !Prompt the user
  993.           call xreio(1,xl,cmIn,-65)       !get a command
  994.           call abreg(i,cmLn)              !Get the input length
  995.           cmCh(cmLn+1:) = ' '             !Clear unused part of command
  996.           cmLn = TrimLen(cmCh)            !Now get the data length
  997.           if (cmLn .lt. 1) goto 20        !Reprompt if none given
  998.       endif
  999.  
  1000. !     This is the top-level parser.  For a given top-level keyword, do the
  1001. !     function indicated.  Most commands can be (severely!) abbreviated.
  1002.  
  1003.       if (fEcho) call tpCh(' -',cmCh,0)   !Echo as directed
  1004.       call kdebug(all,'Command: ',cmCh)   !log the command
  1005.       cmRu = cmCh                         !Save for RunProgram/SetPrompt
  1006.       call CaseFold(cmCh)                 !Convert to upper case
  1007.       do i = 1,cmLn                       !Convert commas to blanks
  1008.           if (cmC1(i) .eq. ',') cmC1(i) = ' '
  1009.       enddo
  1010.       cmLn = TrimLen(cmCh)                !"Kill" trailing blanks
  1011.       if (cmLn .lt. 1) goto 10            !Ignore the current command
  1012.       cmP2 = 0                            !Initialize token search
  1013.       call gettok('?')                    !Locate a token
  1014.  
  1015.       i = match(commands,cmtsiz,0)        !Find the command in vocabulary
  1016.       if (cmTk .eq. '?') goto 30
  1017.       if (i .gt. 0) then                  !If in vocabulary...
  1018.           cmTk = commands(i)              !...expand the command token
  1019.       endif
  1020.  
  1021. !     In the "case" statement which follows, we only return after surviving
  1022. !     the parsing for commands which perform packet-I/O.
  1023.  
  1024.       if (cmTk .eq. 'BYE') then
  1025.           if ( MustBeLocal() ) return     !Only return if local-host
  1026.       else if (cmTk .eq. 'CONNECT') then
  1027.           call connect
  1028.       else if (cmTk.eq.'EXIT' .or. cmTk.eq.'QUIT') then
  1029.           call quit
  1030.           call exec(6)
  1031.       else if (cmTk .eq. 'FINISH') then
  1032.           if ( MustBeLocal() ) return     !Remote-host not allowed
  1033.       else if (cmTk .eq. 'GET') then
  1034.           if ( MustBeLocal() ) then
  1035.               call GetFile(*30)
  1036.               cmTk = 'GET'
  1037.               return
  1038.           endif
  1039.       else if (cmTk .eq. 'HELP') then
  1040.           call help
  1041.       else if (cmTk .eq. 'RECEIVE') then
  1042.           call RecFile(*30)
  1043.           cmTk = 'RECEIVE'
  1044.           return
  1045.       else if (cmTk .eq. 'SEND') then
  1046.           call SndFile(*30)               !Parse the command
  1047.           cmTk = 'SEND'
  1048.           return
  1049.       else if (cmTk .eq. 'SERVER') then
  1050.           call ServerInit(*30)            !Ok to go to server?
  1051.           cmTk = 'SERVER'
  1052.           return
  1053.       else if (cmTk .eq. 'SET') then
  1054.           call Set
  1055.       else if (cmTk .eq. 'SHOW') then
  1056.           call show
  1057.       else if (cmTk .eq. 'STATUS') then
  1058.           call status
  1059.       else if (cmTk .eq. 'TRANSFER') then
  1060.           if (fTrans) then
  1061.               call tpFm('Transfer-files may not be nested!')
  1062.           else
  1063.               call gettok(' ')            !Get transfer-file's name
  1064.               if (FmpOpen(trCb,err,cmTk,'ro',1) .gt. 0) then
  1065.                   fTrans = .true.
  1066.                   call gettok('NO')       !Default to no echo
  1067.                   fEcho = (cmTk(:2) .ne. 'NO')
  1068.               else
  1069.                   call FmpReportError(err,cmTk)
  1070.               endif
  1071.           endif
  1072.       else                                !If no token matches...
  1073.           call RunProgram                 !...assume it is a program name
  1074.       endif
  1075.  
  1076.  30   cmCh = ' '                          !Clear last command out
  1077.       goto 10                             !Get another command
  1078.  
  1079.       end
  1080.  
  1081.       subroutine HostMode                                ,<890525.1144>
  1082.      >Check host mode
  1083.       implicit none
  1084.  
  1085.       include kercom.ftni,NOLIST
  1086.       include kercnf.ftni,NOLIST          !Defines fRmx
  1087.  
  1088.       integer*2 bngdb,i
  1089.       character*5 rates(0:15)
  1090.       data rates/'????','50','75','110','134.5','150','300','1200',
  1091.      >           '1800','2400','4800','9600','19.2k','38.4k','115k',
  1092.      >           'sense'/
  1093.  
  1094.       if (R .eq. 0) then                  !Is remote lu undefined?
  1095.           R = L                           !Assume remote-host mode
  1096.           call MoveWords(LocCnf,RemCnf,CnfSiz) !local config -> remote array
  1097.       endif
  1098.  
  1099.       call tpFm('KERMIT-RTE is in _')
  1100.       call GetPakTimeout(i)               !Sense "remote" baud rate
  1101.       if (R .eq. L) then
  1102.           call tpFm('remote-host mode_')
  1103.           if (.not. fRmx) then
  1104.               call tpFm(', but not on a mux port')
  1105.               call tpFm('(You''ll need to SET LINE to some mux LU_')
  1106.               call tpFm(' before you can transfer files)')
  1107.               call tpFm(' "Server" is not available at this LU!')
  1108.           else
  1109.               call tpFm('; file transfers are ok')
  1110.               if (bngdb() .eq. 0) call lurq(100001b,L,1)
  1111.           endif
  1112.       else
  1113.           call tpI2('local-host mode to LU _',R,0)
  1114.           call tpCh(' @ _',rates(i))      !Show the baud rate
  1115.           call tpFm(' baud; _')
  1116.           call ShowParity
  1117.           call lurq(100001b,R,1)
  1118.       endif
  1119.  
  1120.       return
  1121.       end
  1122.  
  1123.       integer*2 function GetPakTimeout(i)                ,<890525.1144>
  1124.      >Get Packet-I/O timeout
  1125.       implicit none
  1126.  
  1127.       include kercom.ftni,NOLIST
  1128.       include kercnf.ftni,NOLIST
  1129.       integer*2 baudtimes(14),i,ixget
  1130.  
  1131. c     baud rate-->       50,   75,  110,134.5,  150,  300,  1200+
  1132.       data baudtimes / 2500, 1900, 1500, 1300, 1200,  900,  8*600 /
  1133.  
  1134. c     This routine sets my timeout value (in my parameter block) as a
  1135. c     function of the REMOTE's baud-rate.  It returns as it's parameter
  1136. c     the index to the baud-rate table, and as it's value the number of
  1137. c     100ths of a second to wait for a packet (from me) as follows:
  1138. c          the time required to receive 100 bytes at the given baud rate
  1139. c        + 1 second for any fractional second from the above
  1140. c        + 5 seconds for processing time.
  1141.  
  1142. c     NOTE: the remote KERMIT will tell me (in the SINIT packet) if I
  1143. c     should time-out a packet-receive; even if the remote KERMIT doesn't
  1144. c     want that processing, I'll still time-out a packet receive after
  1145. c     30 seconds.  Note that in any event, under RTE-A no receive-packet
  1146. c     timeouts will occur because the device driver must be bypassed.
  1147.  
  1148.       i = r30c                            !Get the current configuration
  1149.       i = iand(ishft(i,-3),17b)           !Isolate/right-justify rate
  1150.       if (i.lt.1 .or. i.gt.12) then       !Is it a legal value?
  1151.           i = 0                           !No - return "unknown"
  1152.           Timeout = 0                     !...and don't do timeouts
  1153.       else
  1154.           Timeout = baudtimes(i)/100      !Put in my parameter block
  1155.       endif
  1156.  
  1157.       GetPakTimeout = Timeout * 100       !Tell SERVER the timeout value
  1158.  
  1159.       return
  1160.       end
  1161.  
  1162.       subroutine ShowParity                              ,<890525.1144>
  1163.      >Show remote-port parity
  1164.       implicit none
  1165.  
  1166.       include kercom.ftni.NOLIST
  1167.       include kercnf.ftni.NOLIST
  1168.       include kcmnds.ftni.NOLIST
  1169.       integer*2 i,j,k,pmask,pvals(5,2)
  1170.       parameter (pmask = 141400b)         !Mask removes current parity bits
  1171.       equivalence (pvalu,pvals)
  1172.  
  1173.       i = r30c .and. pmask                !Isolate current parity bits
  1174.       j = (iRmx .and. 1) + 1              !Set pvalu index for B/C or D mux
  1175.       call tpFm(' Parity = _')
  1176.       do k = 1,prtsiz
  1177.           if (i .eq. pvals(k,j)) then
  1178.               call tpFm(parits(k))
  1179.               return
  1180.           endif
  1181.       enddo
  1182.       call tpFm(' Unknown!')
  1183.  
  1184.       return
  1185.       end
  1186.  
  1187.       logical function ctoi(rval)                        ,<890525.1144>
  1188.      >Parse ASCII to integer
  1189.       implicit none
  1190.  
  1191. c     This routine parses ASCII characters into a single 16-bit integer.
  1192. c     The character data may be in decimal, octal ("B" must be the last
  1193. c     byte), or hexadecimal ("H" must be the last byte), and may contain
  1194. c     a leading sign.  A character-literal may be entered as a number if
  1195. c     the other (last) character is a '"'.  CTOI returns false if no
  1196. c     numeric can be parsed due to no data or illegal data.  The
  1197. c     command-line pointer cmP2 is expected to point to the end of the
  1198. c     previous token on entry; cmP1 and cmP2 will point to the next
  1199. c     token on exit, or cmP1 will equal 0 if there are no more tokens
  1200. c     on the line.
  1201.  
  1202.       include kercmd.ftni,NOLIST
  1203.       integer*2 p,i,e,rval,base
  1204.       character*1 c,s
  1205.  
  1206.       ctoi = .false.                      !Show initial "no number found"
  1207.       rval = 0                            !...and clear the value save
  1208.       call gettok(' ')                    !Isolate the numeric
  1209.       if (cmP1 .lt. 1) return             !there isn't a number there
  1210.  
  1211.       e = cmP2                            !Save end pointer
  1212.       c = cmCh(e:e)                       !Get base character (if any)
  1213.       if (c .eq. 'B') then                !Is it octal?
  1214.           base = 8                        !Yes
  1215.           e = e - 1                       !Don't parse the "b"
  1216.       else if (c .eq. 'H') then           !No? try hexadecimal
  1217.           base = 16
  1218.           e = e - 1                       !Don't parse the "h"
  1219.       else if (c .eq. '"') then           !No? try ASCII literal
  1220.           if ( (cmP2-cmP1) .eq. 1) then   !(must be the literal, then a '"')
  1221.               c = cmCh(cmP1:cmP1)         !Get the character
  1222.               rval = ichar(c)             !Convert to integer
  1223.               ctoi = .true.
  1224.               return
  1225.           endif
  1226.       else                                !No? assume decimal
  1227.           base = 10
  1228.       endif
  1229.  
  1230.       p = cmP1                            !Save the starting pointer
  1231.       c = cmCh(p:p)                       !Get sign (if any)
  1232.       if (c.eq.'+' .or. c.eq.'-') then    !If there is a sign...
  1233.           p = p + 1                       !Bump the byte pointer
  1234.           s = c                           !...and save the sign byte
  1235.       else
  1236.           s = ' '
  1237.       endif
  1238.  
  1239.       do while (p .le. e)                 !Parse the number
  1240.           c = cmCh(p:p)                   !Get a byte
  1241.           p = p + 1                       !Bump the byte pointer
  1242.           i = ichar(c) - 60b              !Convert numerics to integer
  1243.           if (i .lt. 0) return            !non-numeric found
  1244.           if (i.gt.9) then                !Allow possible hex digits
  1245.               if (i.lt.17 .or. i.gt.22) return  !Insure it is 'A' thru 'F'
  1246.               i = i - 7                         !Scale hex digits
  1247.           endif
  1248.           if (i .ge. base) return         !Illegal byte for this base
  1249.           rval = (rval*base) + i          !Continue forming number
  1250.       end do
  1251.  
  1252.       ctoi = .true.
  1253.       if (s .eq. '-') rval = -rval        !Deal with a "-", if found
  1254.       return
  1255.       end
  1256.  
  1257.       subroutine gettok(default)                         ,<890525.1144>
  1258.      >Get a command-line token
  1259.       implicit none
  1260.  
  1261. c     This routine is sets cmP1 and cmP2 to the start and end of a "token"
  1262. c     (one or more non-blanks in the command line), and cmTk is set to the
  1263. c     token itself.  If there are no more tokens in the line, cmP1 returns
  1264. c     zero, cmP2 is undefined, and cmTk returns the "default" string.
  1265. c
  1266. c     NOTICE -- cmP1 is always set to cmP2+1 before use; to locate the
  1267. c     first token in a line you must set cmP2 to 0.  Locating the "next"
  1268. c     token on a line is automatic.
  1269.  
  1270.       include kercmd.ftni,NOLIST
  1271.       character*(*) default
  1272.  
  1273.       cmP1 = cmP2 + 1                     !Always go to next byte
  1274.       call skipbl(cmP1)                   !Skip leading blanks
  1275.       cmP2 = cmP1                         !We have start-of-token
  1276.       call skip2bl(cmP2)                  !go 1 past end-of-token
  1277.       cmP2 = cmP2 - 1                     !Back up to end-of-token
  1278.       if (cmP1 .gt. cmLn) then            !Are we past last token?
  1279.           cmP1 = 0                        !Yes - note it
  1280.           cmTk = default
  1281.       else
  1282.           cmTk = cmCh(cmP1:cmP2)
  1283.       endif
  1284.  
  1285.       return
  1286.       end
  1287.  
  1288.       integer*2 function match(tabl,tlen,dum)            ,<890525.1144>
  1289.      >Match token to token-table
  1290.       implicit none
  1291.  
  1292.       include kercmd.ftni,NOLIST
  1293.       include kercom.ftni,NOLIST
  1294.       character*(*) tabl(*)
  1295.       integer*2 tlen,dum
  1296.       logical*2 fCMMD                     !True for special COMMAND call
  1297.       character*1 tc
  1298.       integer*2 t1,t2,len,TrimLen
  1299.  
  1300. c     MATCH tries to locate the token (cmTK) in the TABLe of strings
  1301. c     containing TLEN entries.  If the token is found >uniquely< in
  1302. c     the table, the index of that entry is returned as the value of
  1303. c     MATCH; otherwise MATCH returns 0.  If the token contains a "?",
  1304. c     any table entries which were matched up to (but not including)
  1305. c     the "?" are printed on locally with an appropriate message.
  1306. c     In the absence of a "?", if more than one table entry matches
  1307. c     the token, MATCH returns as if there was no match and prints a
  1308. c     message to inform the user of ambiguous data and shows all of
  1309. c     the possible choices.
  1310. c
  1311. c     cmTk must not contain embedded blanks
  1312. c     TABL must be in alphabetical order
  1313.  
  1314.       fCMMD = pcount() .eq. 3             !True if special COMMAND call
  1315.       len = TrimLen(cmTk)                 !Get the token's length
  1316.       t1 = 1                              !Set current low table index
  1317.       t2 = tlen                           !Set current high index
  1318.       p = 1                               !Set token byte-pointer
  1319.       match = 0                           !Show no match initially
  1320.  
  1321.       do while (p .le. len)               !Begin matching here
  1322.           tc = cmTk(p:p)                  !Get a token character
  1323.           if (tc .eq. '?') then           !If "?" then give possibilities
  1324.               call tpFm('The following are legal at this point:')
  1325.               call outtbl(tabl,t1,t2)
  1326.               return
  1327.           endif
  1328.  
  1329. c         (do while token is less than lower table entry)
  1330.           do while (tc.gt.tabl(t1)(p:p) .and. t1.le.t2)
  1331.               t1 = t1 + 1
  1332.           enddo
  1333.  
  1334. c         (do while token is greater than upper table entry)
  1335.           do while (tc.lt.tabl(t2)(p:p) .and. t2.ge.t1)
  1336.               t2 = t2 - 1
  1337.           enddo
  1338.  
  1339. c         (if we know we have a mismatch...)
  1340.           if (t2 .lt. t1) then
  1341.               if ( fCMMD ) return
  1342.               call tpCh('No such command or parameter -',cmTk(:len),0)
  1343.               call tpFm('The following are legal at this point:')
  1344.               call outtbl(tabl,1,tlen)
  1345.               return
  1346.           endif
  1347.  
  1348.           p = p + 1                       !Bump the token byte-pointer
  1349.  
  1350.       enddo
  1351.  
  1352. c     After scanning all of the token, is it still ambiguous?
  1353.  
  1354.       if (T1 .ne. T2) then
  1355.           if ( fCMMD ) return
  1356.           call tpCh('"_',cmTk,len)
  1357.           call tpFm('" is ambiguous; possible matches are:')
  1358.           call outtbl(tabl,t1,t2)
  1359.       else
  1360.           match = t1
  1361.           cmTk = tabl(t1)                 !Expand the token
  1362.       endif
  1363.  
  1364.       return
  1365.       end
  1366.  
  1367.       subroutine outtbl(tabl,t1,t2)                      ,<890525.1144>
  1368.      >Print strings (tabular)
  1369.       implicit none
  1370.  
  1371.       character*(*) tabl(*)
  1372.       integer*2 t1,t2
  1373.       integer*2 cwid,ncol,i,j,k
  1374.  
  1375. c     This routine prints strings in a table (TAB) from indexes T1 to T2
  1376.  
  1377.       cwid = len(tabl(1))                 !Get the column width
  1378.       ncol = 80 / (cwid + 2)              !Get # of displayable columns
  1379.  
  1380.       do i = t1,t2,ncol                   !Output the columns
  1381.           do j = 1,ncol                   !print each table entry
  1382.               k = i + j - 1
  1383.               if (k .le. t2) then
  1384.                   call tpCh(' _',tabl(k),cwid)
  1385.                   if (j .lt. ncol) call tpFm(' _')
  1386.               endif
  1387.           enddo
  1388.           call tpFm('-')
  1389.       enddo
  1390.  
  1391.       return
  1392.       end
  1393.  
  1394.       subroutine connect                                 ,<890525.1144>
  1395.      >Do terminal-emulation
  1396.       implicit none
  1397.  
  1398.       include kconcw.ftni,NOLIST          !Defines XLUEX ConWords + term
  1399.       include kercom.ftni,NOLIST
  1400.       include kercmd.ftni,NOLIST
  1401.       include kercnf.ftni,NOLIST
  1402.       include kerdbg.ftni,NOLIST
  1403.       integer*2 ib(128),il                !The input buffer & its length
  1404.       integer*2 k,a,b,gtnw                !Various temporaries
  1405.       logical*2 eflag                     !TRUE if escape-char processed
  1406.       logical*2 NeedRTerm                 !TRUE if Remote needs termination
  1407.       logical*2 ifbrk,SetLine,MustBeLocal
  1408.  
  1409.       k = cmP2                            !Save current parsing pointer
  1410.       call GetTok('none')                 !See if there is another parameter
  1411.       if (cmTk .ne. 'none') then          !If so, do Set Line for the user
  1412.           cmP2 = k                        !Restore the parsing pointer
  1413.           cmTk = 'CONNECT'                !...and the "current" command
  1414.           if (.not. SetLine() ) return    !The set line didn't work
  1415.       endif
  1416.  
  1417.       if (.not. MustBeLocal() ) return    !User hasn't Set Line yet
  1418.  
  1419.       eflag = .false.                     !No escape-char seen yet
  1420.       fASI = .not. fLmx                   !Set for Local ASIC-type card
  1421.       fRcm = .not. btest(iRmx,0)          !Note if remote uses B/C mux
  1422.       fLcm = .not. btest(iLmx,0)          !Note if local uses B/C mux
  1423.       if (class .eq. 0) then              !Do we need a class number?
  1424.           call clrq(100001b,class)        !Yes - get one
  1425.           call abreg(a,b)                 !Get return status
  1426.           if (a .lt. 0) then              !Did we get a class # ?
  1427.               call tpFm('Cannot connect: no class numbers available')
  1428.               return
  1429.           else
  1430.               class = ior(class,20000b)   !Set "don't de-allocate" bit
  1431.           endif
  1432.       endif
  1433.  
  1434. !     (Since GtNW is not in common, we must set it here)
  1435.       gtnw = ior(class,100000b)           !Set no-wait bit
  1436.  
  1437.       call cPrep                          !Prepare ports for connect-mode
  1438.  
  1439.       call tpI2('[connecting to LU _',R,0)
  1440.       call tpFm('; return via "control-_')
  1441.       call tpCh(char(EsChar+64),'" then "C"]',0)
  1442.  
  1443.       if ( fASI ) then                    !Non-mux local does class I/O
  1444.           call xluex(17,Lrx,ib,-1,0,0,class)  !Start local read
  1445.       endif
  1446.       if ( fRcm ) NeedRTerm = .true.      !Terminate 1st B/C mux buffer
  1447.  
  1448. !     The connect loop consists of an inner polling-loop and the outer
  1449. !     processing-loop as follows:
  1450. !         The inner polling-loop interrogates the remote status looking
  1451. !             for availability of type-ahead data, and the local keyboard
  1452. !             (via a no-wait class "get" call, or, if on a mux, when some
  1453. !             typed-ahead data becomes available) until one of them shows
  1454. !             data available, or until the break-flag is set (which will
  1455. !             simulate the user typing <escape-character>"C" to close the
  1456. !             connection.  Processing of remote data is handled within
  1457. !             the inner-loop; we exit the inner-loop on receipt of any
  1458. !             keyboard data or if another user "breaks" this KERMIT.
  1459. !         The outer processing-loop has two major divisions: the polling
  1460. !             loop, and local data-handling, which must be checked on a
  1461. !             character-by-character basis for the presence of the escape
  1462. !             character.  An escape character signals the beginning of a
  1463. !             user command to (the local) KERMIT.
  1464. !
  1465. !     The flow of this code is sufficiently complicated that I have elected
  1466. !     to use (gasp!) a few statement numbers rather than the more elegant
  1467. !     (and more cumbersome) if-then-else and unnumbered do-while constructs
  1468. !     found in previous versions of this routine.
  1469. !
  1470. !     I am indebted to Bruce K. Swope, of Intermedics, Inc. in Freeport,
  1471. !     Texas, for his assistance in locating the cause of the "mux lockup".
  1472. !     His ideas on the handling of the remote port have resulted in greatly
  1473. !     reducing (if not completely eliminating) occurrences of this dreaded
  1474. !     condition.
  1475. !
  1476. !     "D" mux handling notes: as you can probably see from the code, the
  1477. !     12040D handles much nicer than the B or C revisions (can't wait for
  1478. !     the M/E/F version!).  The card can buffer up to 1024 bytes, but I
  1479. !     don't want to tie up that much memory; I have elected to never read
  1480. !     more than 256 bytes from remote port at any given time.
  1481.  
  1482.  10   do while (.true.)                   !inner-loop begins
  1483.           if (NeedRTerm) then             !Need to terminate remote?
  1484.               call control(R,term,0)      !Yes - do it, but not more than
  1485.               NeedRTerm = .false.         !...once per input buffer
  1486.           endif
  1487.           call sleep(4)                   !Let other folks run for 40 ms.
  1488.           if ( IfBrk(k) ) goto 40         !Proceed as if "<esc>C" entered
  1489.           if ( fLmx ) then                !Local on mux?
  1490.               call control(L,dstat,0)     !Yes - get local status
  1491.               call abreg(a,il)
  1492.               if (il .gt. 0) then         !Any data available?
  1493.                   call xluex(1,Lrx,ib,-1) !Yes - get only 1 byte...
  1494.                   if ( fLcm ) then        !For local B/C mux...
  1495.                       call control(L,term,0)  !...re-terminate input
  1496.                   endif
  1497.                   il = 1 $ goto 20        !...and process it
  1498.               endif
  1499.           else
  1500.               call exec(21,gtnw,ib,-1)    !Look for keyboard data
  1501.               call abreg(a,il)            !Get the status
  1502.               if (a .gt. 0) goto 20       !Keyboard data is available
  1503.           endif
  1504.           call control(R,dstat,0)         !Request remote status
  1505.           call abreg(a,il)                !Get it back from driver
  1506.           if (il .gt. 0) then             !Data available?
  1507.               il = min(il,256)            !Limit input to 256 bytes
  1508.               call xluex(1,Rrx,ib,-il)    !Yes - get it
  1509.               if ( fLmx ) then            !Local on a mux?
  1510.                   call xluex(2,Ltx,ib,-il)   !Yes - just write
  1511.               else
  1512.                   call clrq(3,class,L)       !Abort pending keyboard read
  1513.                   call xluex(2,Ltx,ib,-il)   !Copy remote data to display
  1514.                   call xluex(17,Lrx,ib,-1,0,0,class)  !Set new keyboard read
  1515.               endif
  1516.               if ( fRcm ) then            !Terminate remote on next pass...
  1517.                   NeedRTerm = .true.      !...if using B or C mux
  1518.               endif
  1519.           endif
  1520.       end do
  1521.  
  1522.  20   if (il .le. 0) goto 30              !(Ignore keyboard timeout)
  1523.  
  1524.       if (fIBM) call xluex(2,Ltx,ib,-1)   !Do local echo as needed
  1525.       k = ishft(ib,-8)                    !Get the local keystroke
  1526.       if (k.gt.140b .and. k.lt.172b) k=k-40b   !Shift to upper-case
  1527.  
  1528.       if (k .eq. EsChar) then             !Escape?
  1529.           eflag = (.not. eflag)           !Yes - toggle the escape flag
  1530.           if (eflag) il = 0               !Send if previous was escape
  1531.       else if (eflag) then                !If last keypress was escape...
  1532.           il = 0                          !say "don't send this"
  1533.           eflag = .false.                 !turn off escape flag
  1534.           if (k .eq. 103b) then           !'C' or 'c': close the connection
  1535.               goto 40
  1536.           else if (k .eq. 122b) then      !'R' or 'r': resume debug logging
  1537.               dblv = iand(dblv,77777b)
  1538.           else if (k .eq. 123b) then      !'S' or 's': stop debug logging
  1539.               dblv = ior(dblv,100000b)
  1540.           else if (k .eq. 63) then        !'?': help
  1541.               call tpFm('        C         = Close the connection')
  1542.               call tpFm('        R         = Resume debug logging')
  1543.               call tpFm('        S         = Suspend debug logging')
  1544.               call tpCh('control-_',char(eschar+64),0)
  1545.               call tpFm(' (again) = send escape to remote')
  1546.               eflag = .true.
  1547.           else
  1548.               call tpFm('Unknown escape function')
  1549.           endif
  1550.       endif
  1551.  
  1552.       if (il .gt. 0) then                 !Anything left to send?
  1553.           call xluex(2,Rtx,ib,-1)         !Send keystroke to remote
  1554.       endif
  1555.  
  1556.  30   if ( fASI ) then                    !reset local (non-mux) read
  1557.           call xluex(17,Lrx,ib,-1,0,0,class)
  1558.       endif
  1559.       goto 10                             !keep looping
  1560.  
  1561.  40   if ( fASI ) then                    !Do non-mux local cleanup
  1562.           call clrq(3,class,L)            !Clear pending local requests
  1563.           a = 0                           !Prepare to kill completed reqs
  1564.           do while (a .ge. 0)
  1565.               call exec(21,gtnw,ib,-1)    !Get any completed requests
  1566.               call abreg(a,il)
  1567.           enddo
  1568.       elseif ( fLcm ) then                !Cleanup from local B/C mux?
  1569.           call control(L,3700b,102000b)   !Yes (RESTORE does the rest
  1570.       endif
  1571.       if ( fRcm ) then                    !Restore from B/C connect
  1572.           call control(R,3300b,22500b)    !cn33: read reconfig on
  1573.           call control(R,3700b,102000b)   !cn37: terminate on CR only
  1574.       endif
  1575.       call control(R,2600b,1)             !cn26: clear (all) card buffers
  1576.       call restore(L)                     !Restore local parameters
  1577.       call enable(L,fLmx)                 !Restore int scheduling
  1578.       call tpFm('[back at KERMIT-RTE]')
  1579.       return
  1580.  
  1581.       end
  1582.  
  1583.       subroutine help                                    ,<890525.1144>
  1584.      >Process HELP commands
  1585.       implicit none
  1586.  
  1587.       include kcmnds.ftni,NOLIST
  1588.       include kercmd.ftni,NOLIST
  1589.       include kercom.ftni,NOLIST
  1590.       integer*2 i,match,err,o
  1591.       integer*2 TrimLen,FmpOpen,FmpSetPosition,FmpRead
  1592.       integer*4 cr,nr,OldP,CurP,NxtP,DoPos
  1593.       logical*2 fExist
  1594.  
  1595.       integer*2 hfCb(16)                  !Help-file control block
  1596.       integer*2 hBuf(128),hInd(18,7)      !Help-file record buffer
  1597.       character*256 hStr
  1598.       character*36  hKey(7)               !Help-file key entries
  1599.       character*24  TheKey
  1600.       equivalence (hBuf(3),hInd,hKey),(hBuf,hStr)
  1601.  
  1602. !     Statement function -- encodes rec# and offset into a double-integer
  1603.       DoPos(cr,o) = ishft(cr,8) + iand(o,377b)
  1604.  
  1605.       call gettok('HELP')                 !Set pointers to 2nd parameter
  1606.  
  1607.       i = match(commands,cmtsiz)
  1608.       if (i .lt. 1) return                !Just drop invalid commands
  1609.       if (cmTk .eq. 'SET') then       !Since SET has parameters...
  1610.           call gettok('<')            !...parse the 3rd parameter
  1611.           if (cmTk .ne. '<') then     !If it wasn't defaulted...
  1612.               i = match(setparms,setsiz)  !...expand the keyword
  1613.               if (i .lt. 1) return    !Drop an unknown set parameter
  1614.           endif
  1615.       endif
  1616.  
  1617.       if (HlpNam .eq. ' ') then           !Have we found the help file?
  1618.           if (fExist('kermit.hlp::system',1)) then   !No - try system dir
  1619.               HlpNam = 'kermit.hlp::system'
  1620.           else if (fExist('kermit.hlp::kermit',1)) then   !then KERMIT's dir
  1621.               HlpNam = 'kermit.hlp::kermit'
  1622.           else if (fExist('kermit.hlp',1)) then      !Then user's dir
  1623.               HlpNam = 'kermit.hlp'
  1624.           else if (fExist('"kermi::0',1)) then       !Last, try FMGR space
  1625.               HlpNam = '"kermi::0'
  1626.           else
  1627.               call tpFm('KERMIT.HLP missing or wrong file-type')
  1628.               return
  1629.           endif
  1630.       endif
  1631.  
  1632.       i = TrimLen(cmTk)                   !Clean up the key
  1633.       if (FmpOpen(hfCb,err,HlpNam,'ro',1).lt.0) then
  1634.           call ReportFileError(err,HlpNam)
  1635.       endif
  1636.       if (FmpRead(hfCb,err,hBuf,256).lt.0) then  !Read 1st record
  1637.           call FmpClose(hfCb,i)
  1638.           call ReportFileError(err,HlpNam)
  1639.       endif
  1640.       OldP = DoPos(0J,0)                  !Set previous position
  1641.       cr = 1                              !Note current record#...
  1642.       nr = 1                              !...and desired ("new") record
  1643.       o = 1                               !...and current offset
  1644.       CurP = DoPos(cr,o)                  !Save "current" position
  1645.       NxtP = DoPos(nr,o)                  !Save "next" position
  1646.  
  1647.  10   if (NxtP .eq. OldP) then            !In an endless loop?
  1648.           call tpFm('Sorry, no help available')  !Yes: kill endless loop
  1649.           return
  1650.       else
  1651.           OldP = CurP                     !Else save where we've been
  1652.           CurP = NxtP
  1653.       endif
  1654.       if (nr .ne. cr) then                !At correct file position?
  1655.           if (cr .ne. 0) then             !Handle 1st record after open
  1656.               if (FmpSetPosition(hfCb,err,nr,-nr).lt.0) then
  1657.                   call FmpClose(hfCb,i)
  1658.                   call ReportFileError(err,HlpNam)
  1659.               endif
  1660.           endif
  1661.           if (FmpRead(hfCb,err,hBuf,256).lt.0) then
  1662.               call FmpClose(hfCb,i)
  1663.               call ReportFileError(err,HlpNam)
  1664.           endif
  1665.           cr = nr                         !Note new current position
  1666.       endif
  1667.       TheKey = hKey(o)
  1668.       if (cmTk .lt. TheKey(:i)) then      !Current token too high?
  1669.           nr = hInd(15,o)                 !Get new record
  1670.           o  = hInd(16,o)                 !...and new offset
  1671.       else if (cmTk .gt. TheKey(:i)) then !Current token too low?
  1672.           nr = hInd(17,o)
  1673.           o  = hInd(18,o)
  1674.       else                                !Found the key, so...
  1675.           nr = hInd(13,o)                 !...get the text rec#
  1676.           o  = hInd(14,o)                 !...and char offset
  1677.           goto 20
  1678.       endif
  1679.       NxtP = DoPos(nr,o)                  !Build next-position value
  1680.       goto 10                             !Look some more
  1681.  
  1682.  20   if (FmpSetPosition(hfCb,err,nr,-nr).lt.0) then
  1683.           call FmpClose(hfCb,i)
  1684.           call ReportFileError(err,HlpNam)
  1685.       endif
  1686.       if (FmpRead(hfCb,err,hBuf,256).lt.0) then
  1687.           call FmpClose(hfCb,i)
  1688.           call ReportFileError(err,HlpNam)
  1689.       endif
  1690.       i = index(hStr(o:),char(4)) - 1     !Locate possible terminator
  1691.       if (i .lt. 1) then                  !No terminator yet
  1692.           call tpCh(hStr(o:) // '-','_',0)!Print the record
  1693.           o = 1                           !...reset the offset
  1694.           nr = nr + 1                     !...and go on to next record
  1695.           goto 20
  1696.       else
  1697.           i = i + o
  1698.           call tpFm(hStr(o:i))
  1699.       endif
  1700.  
  1701.  999  call FmpClose(hfCb,err)
  1702.  
  1703.       return
  1704.       end
  1705.  
  1706.       logical*2 function fExist(name,type)               ,<890525.1144>
  1707.      >Flag file existence and type
  1708.       implicit none
  1709.  
  1710.       character*(*) name
  1711.       integer*2     type,ActType
  1712.  
  1713. c     This routine returns .true. if the named file exists with the
  1714. c     given file-type.  If the file-type is given as a number less
  1715. c     than zero, the type-checking is omitted, and fExist returns
  1716. c     true if the named file exists as any file type
  1717.  
  1718.       include kercom.ftni,NOLIST
  1719.       integer*2 err,FmpOpen,hfCb(16)
  1720.  
  1721.       ActType = FmpOpen(hfCb,err,name,'ro',1)
  1722.       call FmpClose(hfCb,err)
  1723.       if (type .ge. 0) then
  1724.           fExist = (type .eq. ActType)
  1725.       else
  1726.           fExist = (ActType .ge. 0)
  1727.       endif
  1728.  
  1729.       return
  1730.       end
  1731.  
  1732.       subroutine Set                                     ,<890525.1144>
  1733.      >Parse/Perform SET commands
  1734.       implicit none
  1735.  
  1736.       include kcmnds.ftni,NOLIST
  1737.       include kercom.ftni,NOLIST
  1738.       include kercmd.ftni,NOLIST
  1739.       include kerfil.ftni,NOLIST
  1740.       integer*2 i,match
  1741.  
  1742.       call gettok('?')                    !Isolate the parameter name
  1743.       i = match(setparms,setsiz)
  1744.       if (i .lt. 1) then                  !Just return on invalid choice
  1745.           return
  1746.       else
  1747.           cmTk = setparms(i)
  1748.       endif
  1749.  
  1750.       if (cmTk .eq. 'BINARY') then
  1751.           call Tok_On_True(fBnry)
  1752.           if (fBnry .and. Parity.ne.3 .and. .not.f8OK) then
  1753.               fBnry = .false.
  1754.               call tpFm('Can''t do binary transfers')
  1755.           endif
  1756.       else if (cmTk .eq. 'BQUOTE') then
  1757.           call SetBQuote
  1758.       else if (cmTk .eq. 'CHECK') then
  1759.           call SetCheck
  1760.       else if (cmTk .eq. 'DEBUG') then
  1761.           call SetDebug
  1762.       else if (cmTk .eq. 'DELAY') then
  1763.           call SetDelay
  1764.       else if (cmTk .eq. 'ESCAPE') then
  1765.           call SetEscape
  1766.       else if (cmTk .eq. 'IBM') then
  1767.           if (R .eq. L) then
  1768.               call tpFm('SET IBM is illegal in remote-host mode')
  1769.           else
  1770.               call Tok_On_True(fIBM)
  1771.           endif
  1772.       else if (cmTk .eq. 'LINE') then
  1773.           call SetLine
  1774.       else if (cmTk .eq. 'PACKET') then
  1775.           call SetPacket
  1776.       else if (cmTk .eq. 'PARITY') then
  1777.           call SetParity
  1778.       else if (cmTk .eq. 'PROMPT') then
  1779.           call SetPrompt
  1780.       else if (cmTk .eq. 'QUOTE') then
  1781.           call SetQuote
  1782.       else if (cmTk .eq. 'REPEAT') then
  1783.           call SetRepeat
  1784.       else if (cmTk .eq. 'RETRY') then
  1785.           call SetRetry
  1786.       else if (cmTk .eq. 'RMASK') then
  1787.           call gettok(' ')                !Get the server-receive mask
  1788.           rmask = cmTk
  1789.       else if (cmTk .eq. 'SYNC') then
  1790.           call SetSync
  1791.       else if (cmTk .eq. 'WARNING') then
  1792.           call Tok_On_True(fWarn)
  1793.       endif
  1794.  
  1795.       return
  1796.       end
  1797.  
  1798.       subroutine show                                    ,<890525.1144>
  1799.      >Process SHOW command
  1800.       implicit none
  1801.  
  1802.       include kercom.ftni,NOLIST
  1803.       include kerdbg.ftni,NOLIST
  1804.       include kerfil.ftni,NOLIST          !To define fBnry
  1805.  
  1806.       call HostMode                       !Show who's boss
  1807.  
  1808.       if (R .ne. L) then                  !we're in local-host mode
  1809.           call tpCh('ESCAPE character is ^-',char(eschar+64),1)
  1810.           call tpFm('IBM flag is_')
  1811.           if (fIBM) then
  1812.               call tpFm(' ON; prompt char is DC1')
  1813.           else
  1814.               call tpFm(' OFF')
  1815.           endif
  1816.       endif
  1817.  
  1818.       call tpFm('Binary transfers are_')
  1819.       if (fBnry) then
  1820.           call tpFm(' enabled')
  1821.       else
  1822.           call tpFm(' disabled')
  1823.       endif
  1824.       call tpFm('Receiving a duplicate file will be_')
  1825.       if (fWarn) then
  1826.           call tpFm(' aborted')
  1827.       else
  1828.           call tpFm(' allowed')
  1829.       endif
  1830.       call tpI2('File-send delay (seconds) = -',delay/100,0)
  1831.       call tpI2('Checksum type is -',ChkTyp,0)
  1832.       call tpI2('PACKET size is -',paksiz,0)
  1833.       call tpCh('QUOTE is -',char(quote),1)
  1834.       if ( f8OK ) then
  1835.           call tpCh('BQUOTE is -',char(Bit8),1)
  1836.       else
  1837.           call tpFm('Binary quoting is disabled')
  1838.       endif
  1839.       call tpCh('REPEAT is -',char(Repc),1)
  1840.       call tpCh('SYNC is ^-',char(sync+64),1)
  1841.       call tpCh('File transfer-state is -',state,1)
  1842.       if (dbNm .eq. ' ') then
  1843.           call tpFm('The debug log-file is undefined; not debugging')
  1844.       else if (dbLv .eq. 0) then
  1845.           call tpCh('Nothing is being debug-logged to -',dbNm,0)
  1846.       else if (dbLv .eq. ALL) then
  1847.           call tpCh('Everything is being debug-logged to -',dbNm,0)
  1848.       else if (dbLv .eq. STATES) then
  1849.           call tpCh('States are being debug-logged to -',dbNm,0)
  1850.       else if (dbLv .eq. PACKETS) then
  1851.           call tpCh('Packets are being debug-logged to -',dbNm,0)
  1852.       endif
  1853.  
  1854.       return
  1855.       end
  1856.  
  1857.       subroutine status                                  ,<890525.1144>
  1858.      >Give transmission statistics
  1859.       implicit none
  1860.  
  1861.       include kercom.ftni,NOLIST
  1862.       include kersta.ftni,NOLIST
  1863.       integer*4 time,baud,tbytes,work
  1864.       integer*2 hr,min,sec
  1865.  
  1866.       work = tspak + trpak
  1867.       call tpCh(char(10),'Statistics since startup:',0)
  1868.       call tpI2(' Packets sent =_',tspak,7)
  1869.       call tpI2(' Packets received =_',trpak,7)
  1870.       call tpI4(' Total packets =',work,9)
  1871.       call tpI2('  (_',trtry,0)
  1872.       call tpFm(' of the total were retries)')
  1873.  
  1874.       time = endtim - startim             !How long did it take (seconds)
  1875.       hr = time/3600                      !Get time in hours
  1876.       time = time - hr * 3600             !Remove hours from the time
  1877.       min = time/60                       !Get time in minutes
  1878.       sec = time - min * 60               !...and in seconds
  1879.       call tpCh(char(10),'Statistics of last transfer:',0)
  1880.       call tpI2(' Transfer time (hh:mm:ss) =_',hr,3)
  1881.       call tpI2(':_',min,-2)
  1882.       call tpI2(':',sec,-2)
  1883.       call tpI4(' Avg tx-packet size =_',sbytes/spak,3)
  1884.       call tpI4('  Avg rx-packet size =',rbytes/rpak,3)
  1885.  
  1886.       work = spak + rpak
  1887.       call tpI2(' Packets sent =_',spak,7)
  1888.       call tpI2(' Packets received =_',rpak,7)
  1889.       call tpI4(' Total packets =',work,9)
  1890.       call tpI2('  (_',rtry,0)
  1891.       call tpFm(' of the total were retries)')
  1892.  
  1893.       work = sbytes + rbytes
  1894.       call tpI4(' Bytes sent =_',sbytes,9)
  1895.       call tpI4(' Bytes received =_',rbytes,9)
  1896.       call tpI4(' Total bytes =',work,11)
  1897.  
  1898.       work = sovrhd + rovrhd
  1899.       call tpI4(' Send overhead (bytes) =_',sovrhd,9)
  1900.       call tpI4(' Receive overhead (bytes) =',rovrhd,9)
  1901.       call tpI4('  Total overhead (bytes) =',work,0)
  1902.  
  1903.       time = endtim - startim
  1904.       work = (sbytes + rbytes) / time
  1905.       call tpI4(' Total bytes per second: -',work,0)
  1906.  
  1907.       work = ( (sbytes+rbytes) - (sovrhd+rovrhd) ) / time * 10
  1908.       call tpI4('    Effective baud rate: -',work,0)
  1909.  
  1910.       return
  1911.       end
  1912.  
  1913. $alias lurq, NOABORT
  1914.       subroutine RunProgram                              ,<890525.1144>
  1915.      >Process RUN command
  1916.       implicit none
  1917.  
  1918.       include kcmnds.ftni,NOLIST
  1919.       include kercom.ftni,NOLIST
  1920.       include kercmd.ftni,NOLIST
  1921.       integer*2 prams(5),err,FmpRunProgram,TrimLen
  1922.       character*5 RunName
  1923.  
  1924.       if (cmTk .eq. 'RUN') then
  1925.           call GetTok('!')                !See if a program name was given
  1926.       endif
  1927.       if (cmTk .eq. '!') then
  1928.           call tpFm('Usage: [ru ]program [params...]')
  1929.       else
  1930.           call lurq(40000b,L,1,*10)       !Allow other progs to use this LU
  1931.  10       err = FmpRunProgram(cmRu,prams,RunName)
  1932.           if (err .lt. 0) then
  1933.               if (err .eq. -6) then
  1934.                   call match(commands,CmtSiz)
  1935.               else
  1936.                   call FmpReportError(err,cmTk)
  1937.               endif
  1938.           else
  1939.               call exec(14,1,cmIn,-78)    !Get possible run-string
  1940.               call abreg(err,cmLn)        !Get length of that run-string
  1941.               if (err .ne. 0) then        !Was a string returned?
  1942.                   cmLn = 0                !No - clear the length return
  1943.               else
  1944.                   cmLn = min(TrimLen(cmCh),cmLn)
  1945.               endif
  1946.               if (cmLn.gt.0 .or. prams.ne.0) then
  1947.                   call tpCh('"_',RunName)
  1948.                   call tpFm('" has returned the following _')
  1949.                   if (cmLn .gt. 0) then
  1950.                       call tpFm('string:')
  1951.                       call tpFm('  '//cmCh(:cmLn))
  1952.                       if (prams.ne.0) call tpFm('and the following _')
  1953.                   endif
  1954.                   call tpFm('parameters (decimal):')
  1955.                   if (prams .ne. 0) then
  1956.                       do err = 1,5
  1957.                           call tpI2('  _',prams(err),6)
  1958.                       end do
  1959.                   endif
  1960.                   call tpFm(' -')         !Can't send all blanks to tpFm
  1961.               endif
  1962.               call HostMode               !Re-assert locks as needed
  1963.           endif
  1964.       endif
  1965.  
  1966.       return
  1967.       end
  1968.  
  1969.       subroutine SetDebug()                              ,<890525.1144>
  1970.      >Process SET DEBUG commands
  1971.       implicit none
  1972.  
  1973.       include kcmnds.ftni,NOLIST
  1974.       include kercmd.ftni,NOLIST
  1975.       include kercom.ftni,NOLIST
  1976.       include kerdbg.ftni,NOLIST
  1977.       integer*2 match,i,err,FmpOpen,FmpSetEof,DcbOpen
  1978.  
  1979.       call gettok('?')                    !Locate the next token
  1980.       i = match(debugs,dbtsiz)
  1981.       if (i .lt. 1) return                !Ignore bad parameter
  1982.  
  1983.       if (cmTk .eq. 'FILE') then
  1984.           dbLv = 0                        !Changing the file turns it off
  1985.           if (dbNm .ne. ' ') then
  1986.               call FmpClose(dbCb,err)
  1987.               dbNm = ' '
  1988.           endif
  1989.           call gettok(' ')                !Get the file-name pointers
  1990.           if (cmP1 .lt. 1) then           !None supplied?
  1991.               call tpFm('Usage: SET DEBUG FILE <file-name>')
  1992.               return
  1993.           endif
  1994.           dbNm = cmTk                     !Get the name
  1995.           if (FmpOpen(dbCb,err,dbNm,'wco',1) .lt. 0) then
  1996.               dbNm = ' '
  1997.               call ReportFileError(err,dbNm)   !(error on open - tell user)
  1998.           endif
  1999.           if (FmpSetEof(dbCb,err) .lt. 0) then   !Insure we can write
  2000.               call FmpClose(dbCb,err)
  2001.               dbNm = ' '
  2002.               call ReportFileError(err,dbNm)
  2003.           else
  2004.               call FmpRewind(dbCb,err)
  2005.               return
  2006.           endif
  2007.       endif
  2008.  
  2009.       if (DcbOpen(dbCb,err) .ne. 0) then  !Is debug file open?
  2010.           call tpFm('You need to SET DEBUG FILE <file-name> first')
  2011.       else if (cmTk .eq. 'ALL') then
  2012.           dbLv = ALL
  2013.       else if (cmTk .eq. 'STATES') then
  2014.           dbLv = STATES
  2015.       else if (cmTk .eq. 'PACKETS') then
  2016.           dbLv = PACKETS
  2017.       else if (cmTk .eq. 'OFF') then
  2018.           dbLv = 0
  2019.       endif
  2020.  
  2021.       return
  2022.       end
  2023.  
  2024.       subroutine SetDelay                                ,<890525.1144>
  2025.      >Process SET DELAY command
  2026.       implicit none
  2027.  
  2028.       include kercom.ftni,NOLIST
  2029.       integer*2 i
  2030.       logical*2 ctoi
  2031.  
  2032.       if (R .ne. L) then
  2033.           call tpFm('Set Delay is invalid in Local Host mode')
  2034.       else
  2035.           if (ctoi(i)) then
  2036.               if (i .lt. 0) then
  2037.                   call tpFm('Invalid delay value')
  2038.               else if (i .gt. 30) then
  2039.                   call tpFm('Value too big; using 30 seconds')
  2040.                   delay = 3000
  2041.               else
  2042.                   delay = i * 100
  2043.               endif
  2044.           else
  2045.               call tpFm('Usage: SET DELAY <value from 1 to 30>')
  2046.           endif
  2047.       endif
  2048.       return
  2049.       end
  2050.  
  2051.       subroutine SetEscape                               ,<890525.1144>
  2052.      >Process SET ESCAPE command
  2053.       implicit none
  2054.  
  2055.       include kercom.ftni,NOLIST
  2056.       integer*2 i
  2057.       logical*2 ctoi
  2058.  
  2059.       if (R .eq. L) then
  2060.           call tpFm('SET ESCAPE is invalid in Remote Host mode')
  2061.       else
  2062.           if (.not. ctoi(i)) then
  2063.               call tpFm('Usage: SET ESCAPE <control-character code>')
  2064.           else if (i.gt.0 .and. i.lt.32) then
  2065.               eschar = i
  2066.           else
  2067.               call tpFm('The escape must a control character')
  2068.           endif
  2069.       endif
  2070.  
  2071.       return
  2072.       end
  2073.  
  2074.       Logical*2 function SetLine()                       ,<890525.1144>
  2075.      >Process SET LINE command
  2076.       implicit none
  2077.  
  2078.       include kercom.ftni,NOLIST
  2079.       include kercmd.ftni,NOLIST
  2080.       include kercnf.ftni,NOLIST
  2081.       integer*2 rmtlu,lutru,p30val,p30add,idadd,user(3),lu,sylu
  2082.       character*6 cUser
  2083.       character*1 CmdSav
  2084.       integer*2 WhoLockedLu,bngdb
  2085.       logical*2 ctoi
  2086.       equivalence (cUser,user)
  2087.  
  2088.       CmdSav = CmTk                       !Save 1st command character
  2089.       SetLine = .false.                   !"abort" command on error condition
  2090.       call lurq(100000b)                  !Clear all locks
  2091.       if (.not. ctoi(rmtlu)) then         !Get the LU# parameter
  2092.           if (CmdSav .eq. 'C') then       !Called from CONNECT?
  2093.               call tpFm('Usage: CONNECT [<logical unit #>]')
  2094.           else
  2095.               call tpFm('Usage: SET LINE <logical unit #>')
  2096.           endif
  2097.           goto 10
  2098.       endif
  2099.  
  2100.       sylu = lutru(rmtlu)                 !Get the system LU equivalent
  2101.       if (sylu .lt. 1) then               !In user's session?
  2102.           call tpFm('That LU is not in your session')
  2103.           goto 10
  2104.       endif
  2105.  
  2106.       if (R .ne. L) then                  !If switching remote lu's...
  2107.           call control(R,2600b,1)
  2108.           call restore(R)                 !Restore old configuration
  2109.           call enable(R,fRmx)
  2110.           R = 0
  2111.       endif
  2112.  
  2113.       if (lutru(L) .ne. sylu) then
  2114.           R = rmtlu                       !Set new remote-LU
  2115.           call lurq(100001b,R,1)          !Try to lock it
  2116.           call abreg(idadd,lu)            !Get return status from lock
  2117.           if (idadd .ne. 0) then          !Were we successful?
  2118.               idadd = WhoLockedLu(sylu)   !No - find out who has it
  2119.               call IdAddToName(idadd,user,lu)
  2120.               call tpI2('LU _',R,0)
  2121.               call tpCh(' is locked to _',cUser,0)
  2122.               call tpI2('/_',lu,0)
  2123.               call tpFm(' ...Sorry Charlie')
  2124.               R = 0
  2125.               goto 10
  2126.           endif
  2127.           call GetMux(R,RemCnf)           !Get its configuration
  2128.           if (.not. fRmx) then            !Is the LU on a mux?
  2129.               call tpFm('That LU is not on a mux')
  2130.               R = 0
  2131.           else if (r30c .eq. 0) then      !Has the port been configured?
  2132.               call tpFm('That LU has never been configured')
  2133.               R = 0
  2134.           else
  2135.               call disable(R,fRmx)        !Kill remote interrupt-scheduling
  2136.               call KillEnqAck             !Disable ENQ/ACK as needed
  2137.               SetLine = .true.            !OK to continue a Connect...
  2138.           endif
  2139.       endif
  2140.  
  2141.  10   call HostMode
  2142.  
  2143.       return
  2144.       end
  2145.  
  2146.       subroutine SetParity                               ,<890525.1144>
  2147.      >Process SET PARITY command
  2148.       implicit none
  2149.  
  2150.       include kercom.ftni,NOLIST
  2151.       include kercmd.ftni,NOLIST
  2152.       include kcmnds.ftni,NOLIST
  2153.       include kercnf.ftni,NOLIST
  2154.       include kerfil.ftni,NOLIST          !To define fBnry
  2155.  
  2156.       integer*2 i,j,match,pmask,ixget,pvals(5,2)
  2157.       logical*2 MustBeLocal
  2158.       parameter (pmask = 36377b)          !Mask removes current parity bits
  2159.       equivalence (pvalu,pvals)
  2160.  
  2161.       if (.not. MustBeLocal() ) return    !Can't be remote
  2162.  
  2163.       call gettok('?')                    !Get the parity type requested
  2164.       i = match(parits,prtsiz)            !Do we recognize it?
  2165.       if (i .lt. 1) return
  2166.       Parity = i                          !Set B-quoting as needed
  2167.       r30c = r30c .and. pmask             !Get configuration w/o parity
  2168.       j = (iRmx .and. 1) + 1              !Set pvals index
  2169.       r30c = r30c .or. pvals(i,j)         !Plug in desired parity
  2170.       call control(R,3000b,r30c)          !Send it to the card
  2171.       call sleep(100)                     !Let the card catch up (?)
  2172.       call ShowParity                     !Show the changed parity
  2173.  
  2174.       if (parity.ne.3 .and. fBnry .and. .not.f8OK) then
  2175.           call tpFm('Can''t do binary transfers')
  2176.           fBnry = .false.
  2177.       endif
  2178.  
  2179.       return
  2180.       end
  2181.  
  2182.       subroutine SetPacket                               ,<890525.1144>
  2183.      >Process SET PACKET command
  2184.       implicit none
  2185.  
  2186.       include kercom.ftni,NOLIST
  2187.       integer*2 i
  2188.       logical*2 ctoi
  2189.  
  2190.       if (ctoi(i)) then
  2191.           if (i.gt.30 .and. i.lt.95) then
  2192.               PakSiz = i
  2193.           else
  2194.               call tpFm('Packet Size must be from 31 to 94')
  2195.           endif
  2196.       else
  2197.           call tpFm('Usage: SET PACKET <number from 31 to 94>')
  2198.       endif
  2199.  
  2200.       return
  2201.       end
  2202.  
  2203.       subroutine SetCheck                                ,<890525.1144>
  2204.      >Process SET CHECK command
  2205.       implicit none
  2206.  
  2207.       include kercom.ftni,NOLIST
  2208.       integer*2 i
  2209.       logical*2 ctoi
  2210.  
  2211.       if (ctoi(i)) then
  2212.           if (i.gt.0 .and. i.lt.4) then
  2213.               ChkTyp = i
  2214.           else
  2215.               call tpFm('Checksum type must be from 1 to 3')
  2216.           endif
  2217.       else
  2218.           call tpFm('Usage: SET CHECK <number from 1 to 3>')
  2219.       endif
  2220.  
  2221.       return
  2222.       end
  2223.  
  2224.       subroutine SetPrompt                               ,<890525.1144>
  2225.      >Process SET PROMPT command
  2226.       implicit none
  2227.  
  2228.       include kercmd.ftni,NOLIST
  2229.       include kercom.ftni,NOLIST
  2230.  
  2231.       call gettok('Kermit-RTE>')          !Set default
  2232.       if (cmP1 .ne. 0) then               !Was new prompt given?
  2233.           Prompt = cmRu(cmP1:cmP2)        !Yes - retrieve from original line
  2234.       else
  2235.           Prompt = CmTk                   !Else retrieve the default prompt
  2236.       endif
  2237.  
  2238.       return
  2239.  
  2240.       end
  2241.  
  2242.       subroutine SetQuote                                ,<890525.1144>
  2243.      >Process SET QUOTE command
  2244.       implicit none
  2245.  
  2246.       include kercom.ftni,NOLIST
  2247.       integer*2 i
  2248.       logical*2 ctoi
  2249.  
  2250.       if (ctoi(i)) then
  2251.           if (i.gt.32 .and. i.lt.127) then
  2252.               if (i .eq. Bit8) then
  2253.                   call tpFm('Invalid: conflicts with BQUOTE')
  2254.               else if (i .eq. Repc) then
  2255.                   call tpFm('Invalid: conflicts with REPEAT')
  2256.               else
  2257.                   Quote = i
  2258.               endif
  2259.           else
  2260.               call tpFm('Invalid: value must be from 33 to 126')
  2261.           endif
  2262.       else
  2263.           call tpFm('Usage: SET QUOTE <number from 33 to 126>')
  2264.       endif
  2265.  
  2266.       return
  2267.       end
  2268.  
  2269.       subroutine SetBQuote                               ,<890525.1144>
  2270.      >Process SET BQUOTE command
  2271.       implicit none
  2272.  
  2273.       include kercom.ftni,NOLIST
  2274.       include kerfil.ftni,NOLIST          !To define fBnry
  2275.       integer*2 i
  2276.       logical*2 ctoi
  2277.  
  2278.       if (ctoi(i)) then
  2279.           if ( (i.gt.31 .and. i.lt.63) .or.
  2280.      >         (i.gt.95 .and. i.lt.127) ) then
  2281.               if (i .eq. Quote) then
  2282.                   call tpFm(' Invalid: conflicts with QUOTE')
  2283.               else if (i .eq. Repc) then
  2284.                   call tpFm(' Invalid: conflicts with REPEAT')
  2285.               else
  2286.                   Bit8 = i
  2287.                   f8OK = Bit8 .ne. 32     !.true. if 8th-bit quote enabled
  2288.                   if (.not. f8OK) then    !Did they turn it on?
  2289.                       if (fBnry .and. Parity.ne.3) then  !Can we do binary?
  2290.                           fBnry = .false.
  2291.                           call tpFm('Can''t do binary transfers')
  2292.                       endif
  2293.                   endif
  2294.               endif
  2295.           else
  2296.               call tpFm('Invalid: value must be 32-62 or 96-126')
  2297.           endif
  2298.       else
  2299.           call tpFm('Usage: SET BQUOTE <value (32-62 or 96-126)>')
  2300.       endif
  2301.  
  2302.       return
  2303.       end
  2304.  
  2305.       subroutine SetRepeat                               ,<890525.1144>
  2306.      >Process SET REPEAT command
  2307.       implicit none
  2308.  
  2309.       include kercom.ftni,NOLIST
  2310.       integer*2 i
  2311.       logical*2 ctoi
  2312.  
  2313.       if (ctoi(i)) then
  2314.           if ( (i.gt.32 .and. i.lt.63) .or.
  2315.      >         (i.gt.95 .and. i.lt.127) ) then
  2316.               if (i .eq. Quote) then
  2317.                   call tpFm(' Invalid: conflicts with QUOTE')
  2318.               else if (i .eq. Bit8) then
  2319.                   call tpFm(' Invalid: conflicts with BQUOTE')
  2320.               else
  2321.                   Repc = i
  2322.               endif
  2323.           else
  2324.               call tpFm('Invalid: value must be 33-62 or 96-126')
  2325.           endif
  2326.       else
  2327.           call tpFm('Usage: SET REPEAT <value (33-62 or 96-126)>')
  2328.       endif
  2329.  
  2330.       return
  2331.       end
  2332.  
  2333.       subroutine SetRetry                                ,<890525.1144>
  2334.      >Process SET RETRY command
  2335.       implicit none
  2336.  
  2337.       include kercom.ftni,NOLIST
  2338.       include kercmd.ftni,NOLIST
  2339.       integer*2 i
  2340.       logical*2 ctoi,fInit
  2341.  
  2342.       if (ctoi(i)) then
  2343.           call GetTok(' ')                !Look for (I)nitial
  2344.           fInit = cmTk(1:1) .eq. 'I'
  2345.           if (i.lt.5 .or. i.gt.30) then
  2346.               call tpFm('Invalid: value must be from 5 to 30')
  2347.           else
  2348.               if ( fInit ) then
  2349.                   ImxTry = i
  2350.               else
  2351.                   maxtry = i
  2352.               endif
  2353.           endif
  2354.       else
  2355.           call tpFm('Usage: SET RETRY <number from 5 to 30> [I]')
  2356.       endif
  2357.  
  2358.       return
  2359.       end
  2360.  
  2361.       subroutine SetSync                                 ,<890525.1144>
  2362.      >Process SET SYNC command
  2363.       implicit none
  2364.  
  2365.       include kercom.ftni,NOLIST
  2366.       integer*2 i
  2367.       logical*2 ctoi
  2368.  
  2369.       if (ctoi(i)) then
  2370.           if (i.lt.1 .or. i.gt.31) then
  2371.               call tpFm('Invalid: value must be from 1 to 31')
  2372.           else if (i.eq.EOLch .or. i.eq.17) then
  2373.               call tpFm('Conflicts with EOL or IBM-PROMPT')
  2374.           else
  2375.               sync = i
  2376.           endif
  2377.       else
  2378.           call tpFm('Usage: SET SYNC <number from 1 to 31>')
  2379.       endif
  2380.  
  2381.       return
  2382.       end
  2383.  
  2384.       subroutine Skip2Bl(ptr)                            ,<890525.1144>
  2385.      >Skip to blanks in commands
  2386.       implicit none
  2387.  
  2388. c     This routine finds the first blank in cmCh past the current
  2389. c     PTR position (if any) and returns it in PTR.  If there are no
  2390. c     blank characters from PTR to the end of the string, PTR is
  2391. c     returned pointing to the end of the string + 1.
  2392.  
  2393.       include kercmd.ftni,NOLIST
  2394.       integer*2 ptr
  2395.  
  2396.       do while (ptr .le. cmLn)            !Stay in the string
  2397.           if (cmCh(ptr:ptr) .eq. ' ') then
  2398.               return
  2399.           else
  2400.               ptr = ptr + 1
  2401.           endif
  2402.       end do
  2403.  
  2404.       return
  2405.       end
  2406.  
  2407.       subroutine SkipBl(ptr)                             ,<890525.1144>
  2408.      >Skip blanks in commands
  2409.       implicit none
  2410.  
  2411. c     This routine finds the first non-blank in cmCh past the current
  2412. c     PTR position (if any) and returns it in PTR"  If there are no
  2413. c     non-blank characters from PTR to the end of the string, PTR is
  2414. c     returned unchanged.
  2415.  
  2416.       include kercmd.ftni,NOLIST
  2417.       integer*2 i,ptr
  2418.  
  2419.       i = ptr
  2420.       do while (i .le. cmLn)              !Stay in the string
  2421.           if (cmCh(i:i) .ne. ' ') then
  2422.               ptr = i
  2423.               return
  2424.           else
  2425.               i = i + 1
  2426.           endif
  2427.       end do
  2428.  
  2429.       return
  2430.       end
  2431.  
  2432.       subroutine tok_on_true(flag)                       ,<890525.1144>
  2433.      >Flag if next token = "ON"
  2434.       implicit none
  2435.  
  2436. c     This routine sets its parameter "true" if the next command-line
  2437. c     token is "ON"; or sets it "false" if that token is "OFF".  If
  2438. c     the token is neither of these, subroutine MATCH prints a message,
  2439. c     and this routine leaves its parameter alone.
  2440.  
  2441.       logical*2   flag
  2442.       character*3 on_off(2)
  2443.       integer*2   match,i
  2444.  
  2445.       data on_off/'OFF','ON'/
  2446.  
  2447.       call gettok('?')                    !Locate next token
  2448.       i = match(on_off,2)                 !Match it to "ON" or "OFF"
  2449.       if (i .eq. 1) then                  !If "OFF" matched...
  2450.           flag = .false.                  !...do the "off" thing
  2451.       else if (i .eq. 2) then             !If "ON" matched...
  2452.           flag = .true.                   !...do the "on" thing
  2453.       endif                               !Else do nothing
  2454.  
  2455.       return
  2456.       end
  2457.  
  2458.  
  2459.       subroutine SndFile(*)                              ,<890525.1144>
  2460.      >Send file(s)
  2461.       implicit none
  2462.  
  2463.       include kercmd.ftni,NOLIST          !defines cmTk
  2464.       include kercnf.ftni,NOLIST          !defines fRmx
  2465.       include kerfil.ftni,NOLIST          !defines mask, first, and fiNm
  2466.  
  2467. !     This routine was split into a command-processing section (this one)
  2468. !     and a packet-sending section (in the main) for revision 1.99.
  2469.  
  2470.       if (.not. fRmx) then                !Are transfers ok?
  2471.           call tpFm('You need to SET LINE first')
  2472.           return 1
  2473.       endif
  2474.  
  2475.       call gettok(' ')                    !Get the ind-file or mask
  2476.       if (cmTk .eq. ' ') then             !None given?
  2477.           call tpFm('Usage: SEND <file-descriptor>')
  2478.           return 1
  2479.       else
  2480.           mask = cmTk
  2481.       endif
  2482.  
  2483.       call gettok(' ')                    !Look for 2nd file name (initial)
  2484.       fiNm = cmTk
  2485.  
  2486.       first = .true.
  2487.       return
  2488.  
  2489.       end
  2490.  
  2491.       subroutine RecFile(*)                              ,<890525.1144>
  2492.      >Receive file(s)
  2493.       implicit none
  2494.  
  2495.       include kercmd.ftni,NOLIST          !Defines cmTk
  2496.       include kercnf.ftni,NOLIST          !Defines fRmx
  2497.       include kerfil.ftni,NOLIST          !Defines mask and first
  2498.  
  2499. !     This routine was split into a command-parsing section (this one) and
  2500. !     a packet-receiving section (in the 3rd segment) at revision 1.99.
  2501.  
  2502.       if (.not. fRmx) then                !OK to transfer files?
  2503.           call tpFm('You need to SET LINE first')
  2504.           return 1
  2505.       endif
  2506.  
  2507.       call gettok(' ')                    !Get the optional local name
  2508.       first = .true.
  2509.       mask = cmTk
  2510.       return
  2511.  
  2512.       end
  2513.  
  2514.       subroutine GetFile(*)                              ,<890525.1144>
  2515.      >Receive from a server
  2516.       implicit none
  2517.  
  2518.       include kercmd.ftni,NOLIST          !Defines cmTk
  2519.       include kerfil.ftni,NOLIST          !Defines GetMask, mask, and first
  2520.  
  2521.       call gettok(' ')                    !Get the (remote) file name
  2522.       if (cmTk .eq. ' ') then             !Was one given?
  2523.           call tpFm('Usage: GET <remote-name> [<local-name>]')
  2524.           return 1
  2525.       else
  2526.           GetMask = cmTk                  !Save the mask for the get
  2527.       endif
  2528.  
  2529.       call gettok(' ')                    !Get optional local file-name
  2530.       mask = cmTk
  2531.       first = .true.
  2532.       return
  2533.  
  2534.       end
  2535.  
  2536.       subroutine ServerInit(*)                           ,<890525.1144>
  2537.      >Start a KERMIT Server
  2538.       implicit none
  2539.  
  2540.       include kercom.ftni,NOLIST
  2541.       include kercnf.ftni,NOLIST          !Defines fRmx
  2542.  
  2543.       if (R .ne. L) then                  !If local host...
  2544.           call tpFm('Server not available in local-host mode')
  2545.           return 1
  2546.       else if (.not. fRmx) then
  2547.           call tpFm('You are not on a Mux LU')
  2548.           return 1
  2549.       else
  2550.           fServ = .true.
  2551.           call tpFm('[KERMIT Server running on an HP-1000 host.')
  2552.           call tpFm(' You must escape to your local machine now!]')
  2553.       endif
  2554.       return
  2555.  
  2556.       end
  2557.  
  2558.       logical*2 function MustBeLocal()                   ,<890525.1144>
  2559.      >Insure local-host mode
  2560.       implicit none
  2561.  
  2562.       include kercom.ftni,NOLIST
  2563.  
  2564.       MustBeLocal = .false.               !Assume we are in remote host
  2565.       if (R .eq. L) then                  !Are we remote?
  2566.           call tpFm('You need to Set Line to a mux port first!')
  2567.       else
  2568.           MustBeLocal = .true.
  2569.       endif
  2570.       return
  2571.  
  2572.       end
  2573.       program K2MSK(5)                                   ,<890525.1144>
  2574.      >KERMIT file-masking
  2575.       implicit none
  2576.  
  2577.       include kercom.ftni,NOLIST          !To define seg
  2578.  
  2579.       seg = 2                             !Keep the segment-loader happy
  2580.       call SegRt
  2581.  
  2582.       end
  2583.  
  2584.       Subroutine NextFile                                ,<890525.1144>
  2585.      >Get next file to send
  2586.       implicit none
  2587.  
  2588. !     The purpose of this routine is to supply an file, opened for reading,
  2589. !     to the packet-I/O handler in masked searches.  A simple file-name is
  2590. !     handled as if it was a mask, which doesn't bother the file-system at
  2591. !     all.  This routine is used only to find files to be sent.
  2592.  
  2593.       include kercom.ftni,NOLIST
  2594.       include kerfil.ftni,NOLIST
  2595.  
  2596.       integer*2 err,dntry(32),j
  2597.       integer*2 FmpInitMask,TrimLen,FmpOpen
  2598.       logical*2 FmpNextMask
  2599.       character*64 name1
  2600.       character*5 openopts
  2601.  
  2602.  
  2603.       if (first) then                     !Do 1st-entry stuff if needed
  2604.  
  2605.           ! On masked searches, 1st entry consists of initializing
  2606.           ! the search mask.  Note that, on error, Error will re-
  2607.           ! turn to the caller if we are a KERMIT server; otherwise,
  2608.           ! execution will continue in the command-processor.
  2609.           ! If the initial file-name was given, then we copy it to
  2610.           ! Name1 and delay clearing the First flag.
  2611.  
  2612.           call FmpEndMask(maCb)           !Be sure old mask is closed
  2613.           if (FmpInitMask(maCb,fiEr,mask,CurPath,372) .lt. 0)
  2614.      >        call ReportFileError(fiEr,mask)
  2615.           if (fiNm .ne. ' ') then         !Was an initial name given?
  2616.               name1 = fiNm                !Yes - save it
  2617.           else
  2618.               first = .false.             !else no initial-name search
  2619.           endif
  2620.  
  2621.       endif
  2622.  
  2623.       ! Normal processing on masked sends involves getting the next
  2624.       ! name, as above, but the file-system will tell us if there
  2625.       ! is anything else to find.  If an initial file-name was given
  2626.       ! we will attempt to match it here.
  2627.  
  2628.   10  fMore = FmpNextMask(maCb,fiEr,CurPath,dntry)
  2629.       fiNm = ' '
  2630.       if (fiEr .lt. 0) then               !On mask error...
  2631.           if (fiEr.ne.-208) then          !(except duplicate directory)
  2632.               call ReportFileError(fiEr,CurPath)
  2633.           else
  2634.               if (fMore) goto 10          !Get another name, if possible
  2635.               goto 20
  2636.           endif
  2637.       else
  2638.           if (fMore) then
  2639.               call FmpMaskName(maCb,fiNm,dntry,CurPath)
  2640.           else
  2641.               goto 20
  2642.           endif
  2643.       endif
  2644.  
  2645.       if (first) then                     !Doing initial-name search?
  2646.           j = TrimLen(name1)              !Use only as much as user gave
  2647.           if (name1 .eq. fiNm(:j)) then   !Now check: found it yet?
  2648.               first = .false.             !Yes - stop searching
  2649.           else
  2650.               goto 10                     !not found yet - keep searching
  2651.           endif
  2652.       endif
  2653.  
  2654. !     If we have a file, open it and return the name to the caller
  2655.  
  2656.  20   if (fiNm .ne. ' ') then
  2657.           if (fBnry) then
  2658.               openopts = 'rofx'
  2659.           else
  2660.               openopts = 'ro'
  2661.           endif
  2662.           if (FmpOpen(fiCb,fiEr,fiNm,openopts,1) .lt. 0) then
  2663.               call ReportFileError(fiEr,fiNm)
  2664.               fiNm = ' '
  2665.           else
  2666.               fiPt = 0                    !Force 1st record to be read
  2667.           endif
  2668.       endif
  2669.  
  2670.       return                              !Return to main
  2671.  
  2672.       end
  2673.       program K3XFR(5)                                   ,<890525.1144>
  2674.      >KERMIT file-transfer
  2675.       implicit none
  2676.  
  2677.       include kercom.ftni,NOLIST          !To define seg
  2678.  
  2679.       seg = 3
  2680.       call SegRt
  2681.  
  2682.       end
  2683.       subroutine bye                                     ,<890525.1144>
  2684.      >BYE command processor
  2685.       implicit none
  2686.  
  2687.       include kercom.ftni,NOLIST
  2688.       integer retry,err,num,len
  2689.       logical*2 fRtryi
  2690.       character*1 ptype,RecPack
  2691.  
  2692. c     This routine sends a generic-logout packet to a server running on
  2693. c     another system.  On success, we terminate gracefully.
  2694.  
  2695.       pdata = 'L'                         !Set data to L(ogout)
  2696.       retry = 0                           !Reset retry counter
  2697.  
  2698.  10   if ( fRtryi(retry) ) then           !Exceeded retry limit?
  2699.           call tpFm('Unable to send LOGOUT')
  2700.           return                          !Go back to command processor
  2701.       endif
  2702.  
  2703.       call SndPack('G',seq,1)             !Send the logout packet
  2704.       ptype = RecPack(len,num)            !Get the response
  2705.  
  2706.       if (ptype .eq. 'T') then            !time-out?
  2707.           call tpFm('<timed out>')
  2708.           goto 10
  2709.  
  2710.       else if (ptype .eq. 'N') then       !NAK?
  2711.           if (mod(seq+1,64) .ne. num) goto 10
  2712.           ptype = 'Y'
  2713.           num = num - 1
  2714.       endif
  2715.  
  2716.       if (ptype .eq. 'Y') then            !ACK?
  2717.           if (seq .ne. num) goto 10
  2718.           call quit                       !Shut ourselves down
  2719.           Stop
  2720.       else if (ptype .eq. 'X') then       !Checksum error?
  2721.           call tpFm('Checksum error; retrying')
  2722.       else if (ptype .eq. 'E') then       !Error packet?
  2723.           call tpCh('Received error packet: -',RecPkt(5:len+4),0)
  2724.           return
  2725.       else
  2726.           call tpCh('Unknown packet type: -',ptype,1)
  2727.       endif
  2728.       goto 10
  2729.  
  2730.       end
  2731.  
  2732.       subroutine finish                                  ,<890525.1144>
  2733.      >FINISH command processor
  2734.       implicit none
  2735.  
  2736.       include kercom.ftni,NOLIST
  2737.       integer*2 retry,err,len,num
  2738.       logical*2 fRtryi
  2739.       character*1 ptype,RecPack
  2740.  
  2741.       pdata = 'F'                         !Set data to F(inish)
  2742.       retry = 0
  2743.  
  2744.  10   if ( fRtryi(retry) ) then
  2745.           call tpFm('Unable to FINISH')
  2746.           return
  2747.       endif
  2748.  
  2749.       call SndPack('G',seq,1)             !Send the finish packet
  2750.       ptype = RecPack(len,num)
  2751.  
  2752.       if (ptype .eq. 'T') then            !time-out?
  2753.           call tpFm('<timed out>')
  2754.           goto 10
  2755.  
  2756.       else if (ptype .eq. 'N') then       !NAK?
  2757.           if (mod(seq+1,64) .ne. num) goto 10
  2758.           ptype = 'Y'
  2759.           num = num - 1
  2760.       endif
  2761.  
  2762.       if (ptype .eq. 'Y') then            !ACK?
  2763.           if (num .ne. seq) goto 10
  2764.           return
  2765.       else if (ptype .eq. 'X') then       !Checksum error?
  2766.           call tpFm('Checksum error - retrying')
  2767.       else if (ptype .eq. 'E') then       !Error packet?
  2768.           call tpFm('Received error packet:')
  2769.           call tpFm(RecPkt(5:len+4))
  2770.           return
  2771.       else
  2772.           call tpCh('Unknown packet type: -',ptype,1)
  2773.       endif
  2774.  
  2775.       goto 10
  2776.  
  2777.       end
  2778.  
  2779.       subroutine Logoff3                                 ,<890525.1144>
  2780.      >(LogOff caller)
  2781.       implicit none
  2782.  
  2783.       call LogOff                         !We never return...
  2784.       call exec(6)                        !(just in case)
  2785.  
  2786.       end
  2787.  
  2788.       logical*2 function receive(istate)                 ,<890525.1144>
  2789.      >Receive-state switch
  2790.       implicit none
  2791.  
  2792. c     This routine performs state switching for file-receive operations.
  2793. c     If file-recepetion is successful, receive returns .true.
  2794.  
  2795.       include kercom.ftni,NOLIST
  2796.       include kerdbg.ftni,NOLIST
  2797.       include kersta.ftni,NOLIST
  2798.       character*(*) istate
  2799.       integer*2 retry
  2800.       character*1 rdata,rinit,rfile
  2801.       character*3 c_r
  2802.  
  2803.       state = istate                      !Show receiving state
  2804.       call startstats                     !restart statistics logging
  2805.       retry = 0
  2806.       c_r = char(13) // ' _'
  2807.       receive = .false.
  2808.  
  2809.  10   call kdebug(states,'RxState: ',state)
  2810.       if (retry .ne. 0) rtry = rtry + 1
  2811.       if (R .ne. L) then
  2812.           call tpI2(c_r,rpak,6)
  2813.           call tpI2('/_',rtry,-3)
  2814.           call tpFm(' _-')
  2815.       endif
  2816.  
  2817.       if (state .eq. 'D') then            !read a DATA packet
  2818.           state = rdata(retry)
  2819.       else if (state .eq. 'R') then       !read a SINIT packet
  2820.           state = rinit(retry)
  2821.       else if (state .eq. 'F') then       !read a file header
  2822.           state = rfile(retry)
  2823.       else if (state .eq. 'C') then       !file transfer complete?
  2824.           call endstats                   !turn off statistics logging
  2825.           sCheck = 1                      !Revert to type-1 checksums
  2826.           receive = .true.
  2827.           return
  2828.       else if (state .eq. 'E') then       !We received an error packet
  2829.           call endstats
  2830.           call fClose
  2831.           if (.not. fServ) call tpFm(ErrMsg)
  2832.           return
  2833.       else if (state .eq. '!') then       !we got an error
  2834.           call endstats
  2835.           call fClose
  2836.           call SndErr
  2837.           return
  2838.       else                                !Unknown receive state
  2839.           call endstats
  2840.           call fClose
  2841.           ErrMsg = 'Receive-state error; state = ' // state
  2842.           call kdebug(states,ErrMsg,' ')
  2843.           if (R .ne. L) then
  2844.               call tpFm(ErrMsg)
  2845.           endif
  2846.           call SndErr
  2847.           return
  2848.       endif
  2849.  
  2850.       goto 10
  2851.  
  2852.       end
  2853.  
  2854.       character*1 function rinit(retry)                  ,<890525.1144>
  2855.      >Receive initial packet
  2856.       implicit none
  2857.  
  2858.       include kercom.ftni,NOLIST
  2859.       include kerfil.ftni,NOLIST          !To define fBnry
  2860.       integer*2 retry,len,num,svCheck
  2861.       logical*2 fRtryi
  2862.       character*1 ptype,recpack
  2863.  
  2864.       rinit = state                       !Assume no state change
  2865.  
  2866.       if ( fRtryi(retry) ) then
  2867.           rinit = '!'                     !exceeded max. # of re-try
  2868.           return                          !give up
  2869.       endif
  2870.  
  2871.       ptype = recpack(len,num)            !read a packet
  2872.       if (ptype .eq. 'S') then            !we got a SINIT packet
  2873.           call RecPar(len)                !store partner's params
  2874.           if (fBnry .and. Parity.ne.3 .and. .not.fBit8) then
  2875.               ErrMsg = 'Can''t receive binary file (parity problem)'
  2876.               call SndErr
  2877.               fBnry = .false.
  2878.               rinit = 'E'
  2879.               return
  2880.           endif
  2881.           call SndPar('Y',num)            !Send my parameters now
  2882.           sCheck = newChk                 !OK to change check type now
  2883.           seq = mod(num+1,64)             !Set new sequence number
  2884.           retry = 0                       !Clear the retry counter
  2885.           rinit = 'F'                     !New state = File header
  2886.       else if (ptype .eq. 'X') then       !we got a checksum error
  2887.           call SndPack('N',num,0)         !NAK the packet
  2888.       else if (ptype .eq. 'T') then       !Timed out?
  2889.           call SndPack('N',seq,0)         !just nak it
  2890.       else if (ptype .eq. 'E') then       !Error packet?
  2891.           ErrMsg = 'Sender error: ' // RecPkt(5:len+4)
  2892.           rinit = 'E'
  2893.       else
  2894.           ErrMsg = 'Unknown packet type: ' // ptype
  2895.           rinit = '!'                     !Unexpected packet, so give up
  2896.       endif
  2897.  
  2898.       return
  2899.       end
  2900.  
  2901.       character*1 function rfile(retry)                  ,<890525.1144>
  2902.      >Read file-header packet
  2903.       implicit none
  2904.  
  2905.       include kercom.ftni,NOLIST
  2906.       include kerfil.ftni,NOLIST
  2907.       integer*2 retry,num,len,FmpOpen,TrimLen,err,typ
  2908.       logical*2 fRetry
  2909.       character*1 recpack,ptype
  2910.       character*5 openopts
  2911.  
  2912.       if ( fRetry(retry) ) then
  2913.           rfile = '!'
  2914.           return
  2915.       endif
  2916.  
  2917.       rfile = state
  2918.       ptype = RecPack(len,num)            !Read a packet
  2919.       if (ptype .eq. 'F') then            !we got a file-header
  2920.           if (num .ne. seq) then          !If the sequence# is bad...
  2921.               ErrMsg = 'Bad sequence number'
  2922.               rfile = '!'                 !...abort
  2923.               return
  2924.           endif
  2925.           pdata = RecPkt(5:len+4)         !copy off the file-name info...
  2926.           call legalize(typ)              !...validate & move it to fiNm
  2927.           openopts = 'wc'                 !min open options: write & create
  2928.           if (.not. fWarn) then           !If files can be overlayed...
  2929.               call chApp(openopts,'o')    !allow (O)ld files
  2930.           endif
  2931.           if (fBnry) then                 !If in binary mode...
  2932.               call chApp(openopts,'f')    !...(F)orce to type 1
  2933.               if (typ .ne. 6) then        !if not an rp-able program...
  2934.                   call chApp(openopts,'x')!...allow e(X)tent access
  2935.               endif
  2936.           endif
  2937.           if (FmpOpen(fiCb,err,fiNm,openopts,1) .lt. 0) then
  2938.               Call ReportFileError(err,fiNm)
  2939.           else
  2940.               fiCh = ' '                  !Clear the record buffer
  2941.               fiPt = 0                    !Set record pointer
  2942.               call SndPack('Y',num,0)
  2943.               seq = mod(seq+1,64)
  2944.               rfile = 'D'                 !Switch to data state
  2945.               if (R.ne.L) then            !Display the file name locally
  2946.                   call tpCh('Receiving -',fiNm,TrimLen(fiNm))
  2947.               endif
  2948.               retry = 0
  2949.           endif
  2950.  
  2951.       else if (ptype .eq. 'S') then       !Old send-init packet?
  2952.           if (mod(num+1,64) .eq. seq) then
  2953.               call SndPar('Y',num)        !Yes - send my parameters
  2954.               retry = 0
  2955.           else
  2956.               ErrMsg = 'Bad sequence# on old SINIT'
  2957.               rfile = '!'
  2958.           endif
  2959.  
  2960.       else if (ptype .eq. 'Z') then       !Old EOF packet?
  2961.           if (mod(num+1,64) .eq. seq) then
  2962.               call SndPack('Y',num,0)     !Yes - just ack it
  2963.               retry = 0
  2964.           else
  2965.               ErrMsg = 'Bad sequence# on old EOF'
  2966.               rfile = '!'
  2967.           endif
  2968.  
  2969.       else if (ptype .eq. 'B') then       !Break packet?
  2970.           if (num .ne. seq) then
  2971.               ErrMsg = 'Bad sequence# on BREAK'
  2972.               rfile = '!'
  2973.           else
  2974.               call SndPack('Y',num,0)
  2975.               rfile = 'C'                 !Change state to Complete
  2976.               retry = 0
  2977.           endif
  2978.  
  2979.       else if (ptype .eq. 'X') then       !Checksum error?
  2980.           call SndPack('N',num,0)
  2981.  
  2982.       else if (ptype .eq. 'T') then       !Time-out?
  2983.           call SndPack('N',seq,0)         !just NAK it
  2984.  
  2985.       else if (ptype .eq. 'E') then       !Error packet?
  2986.           ErrMsg = 'Sender error: ' // RecPkt(5:len+4)
  2987.           rfile = 'E'
  2988.  
  2989.       else                                !Invalid packet type
  2990.           ErrMsg = 'Unknown packet type: ' // ptype
  2991.           rfile = '!'
  2992.       endif
  2993.  
  2994.       return
  2995.       end
  2996.  
  2997.       subroutine chApp(dest,newstuff)                    ,<890525.1144>
  2998.      >Append to string
  2999.       implicit none
  3000.  
  3001.       character*(*) dest,newstuff
  3002.       integer*2 TrimLen,i
  3003.  
  3004.       i = TrimLen(dest) + 1           !Find 1st available character
  3005.       if (i .le. Len(dest)) dest(i:) = newstuff
  3006.  
  3007.       return
  3008.       end
  3009.  
  3010.       character*1 function rdata(retry)                  ,<890525.1144>
  3011.      >Receive data packet
  3012.       implicit none
  3013.  
  3014.       include kercom.ftni,NOLIST
  3015.       integer*2 retry,num,len
  3016.       logical*2 fRetry
  3017.       character*1 ptype,recpack
  3018.  
  3019.       rdata = state                       !Assume no change in state
  3020.       if ( fRetry(retry) ) then
  3021.           rdata = '!'
  3022.           return
  3023.       endif
  3024.  
  3025.       ptype = recpack(len,num)            !read a packet
  3026.  
  3027.       if (ptype .eq. 'D') then            !we got the data packet
  3028.           if (num .ne. seq) then          !Sequence error?
  3029.               if (mod(num+1,64).eq.seq) then   !Was prev packet re-sent?
  3030.                   call SndPack('Y',num,0)      !Yes - just ack it
  3031.                   retry = 0
  3032.               else
  3033.                   ErrMsg = 'Bad Sequence#'
  3034.                   rdata = '!'
  3035.               endif
  3036.           else
  3037.               call BufEmp(len)            !Sequence # ok - copy to disc
  3038.               call SndPack('Y',num,0)     !Ack the packet
  3039.               seq = mod(num+1,64)         !Bump the sequence number
  3040.               retry = 0                   !Show no more retries
  3041.           endif
  3042.  
  3043.       else if (ptype .eq. 'F') then       !Old filename packet?
  3044.           if (mod(num+1,64).eq.seq) then  !Yes - sequence# ok?
  3045.               call SndPack('Y',num,0)     !Yes - ack it
  3046.               retry = 0                   !Show no retries
  3047.           else
  3048.               ErrMsg = 'Bad Sequence#'
  3049.               rdata = '!'
  3050.           endif
  3051.  
  3052.       else if (ptype .eq. 'Z') then       !EOF packet?
  3053.           if (num .ne. seq) then          !Yes - sequence# ok?
  3054.               rdata = '!'                 !No - abort the transfer
  3055.           else
  3056.               call SndPack('Y',num,0)     !Yes - ACK it
  3057.               call dputc(-2)              !Post poss pending buffer to file
  3058.               rdata = 'F'                 !Look for another file-header
  3059.               seq = mod(num+1,64)         !Set next sequence number
  3060.               retry = 0
  3061.           endif
  3062.           call fClose
  3063.  
  3064.       else if (ptype .eq. 'X') then       !Checksum error?
  3065.           call SndPack('N',num,0)         !NAK the packet
  3066.  
  3067.       else if (ptype .eq. 'T') then       !time-out?
  3068.           call SndPack('N',seq,0)
  3069.  
  3070.       else if (ptype .eq. 'E') then       !Error packet?
  3071.           ErrMsg = 'Sender error: ' // RecPkt(5:len+4)
  3072.           rdata = 'E'
  3073.  
  3074.       else                                !Unknown packet type?
  3075.           ErrMsg = 'Unknown packet type: ' // ptype
  3076.           rdata = '!'                     !Abort the transfer
  3077.       endif
  3078.  
  3079.       return
  3080.       end
  3081.  
  3082.       subroutine legalize(typ)                           ,<890525.1144>
  3083.      >Insure valid file names
  3084.       implicit none
  3085.  
  3086. c     This routine is called for any received file to insure that its name
  3087. c     is valid in this system.  Name validity has two main components:
  3088. c         * No illegal characters
  3089. c         * Correct specification of directory path
  3090. c     Once validity is insured, this routine copies that name to fiNm, the
  3091. c     global used for files' names in KERMIT-RTE.
  3092. c
  3093. c     The first issue is addressed by editing the file name so that any
  3094. c     occurrences of "+", "-", ",", or "@" are changed to an underscore.
  3095. c     If the first character is numeric, it also is replaced by an under-
  3096. c     score, as are any embedded blanks in the name.  RTE system can only
  3097. c     use a period to separate file-names from the type-extension, if the
  3098. c     other system uses something else, this system won't understand but
  3099. c     won't change it unless it violates one of the rules above.  "/" and
  3100. c     ":" are used in different contexts to describe directory paths in
  3101. c     this system; if they appear in the name sent from the other system,
  3102. c     they and their associated information will be removed!
  3103. c
  3104. c     This routine causes the file to be put into the current working-
  3105. c     directory, or into the directory given by the first parameter of
  3106. c     a RECEIVE command or the second parameter of the GET command.  If
  3107. c     there is no active working-directory and no directory was given
  3108. c     by the RECEIVE or GET command, the file will be put in FMGR-space,
  3109. c     and the file-name will be truncated to the left-most 6 characters.
  3110. c
  3111. c     NOTE: if a full file name is given in GET or RECEIVE, that name is
  3112. c     used for the first file received only; any other files received in
  3113. c     the same stream can be renamed in all parts BUT the file name.
  3114.  
  3115.       include kercom.ftni,NOLIST
  3116.       include kerfil.ftni,NOLIST
  3117.       integer*2   ij,sc,typ,rl,i,TrimLen
  3118.       character   name*16,typx*4,rtpx*4,ds*40,junk*1
  3119.  
  3120.       if (first) then                     !First time after GET or RECEIVE?
  3121.           ds = ' '                        !Clear the DS-information string
  3122.           rtpx = ' '                      !...and the (mask) type-extendion
  3123.           first = .false.                 !Don't do this again
  3124.           if (mask .ne. ' ') then         !If a name was given...
  3125.               fiNm = mask                 !Is it a name and/or a mask?
  3126.               call FmpParsePath(fiNm,mask,name,rtpx,junk,sc,typ,fiSz,
  3127.      >                          rl,ds)    !NOTE: qualifier is illegal here
  3128.                                           !      but DS-stuff is not!
  3129.               call checkname(rtpx)        !Validate the type-extension
  3130.               if (name .ne. ' ') then          !If anything is left...
  3131.                   if (rtpx .ne. ' ') then      !do we also have type-ext?
  3132.                       i = TrimLen(name)
  3133.                       pdata = name(:i) // '.' // rtpx
  3134.                   else
  3135.                       pdata = name
  3136.                   endif
  3137.               endif
  3138.           endif
  3139.           if (typ .lt. 1) typ = 4         !Default to var-length/editable
  3140.           if (fiSz.lt. 1) fiSz = 24       !...and 24 blocks
  3141.           if (rl .lt. 0) rl = 0
  3142.       endif
  3143.  
  3144. c     Directory-, qualifier-, or DS-info will never be sent from another
  3145. c     KERMIT, but I parse for them in the following call (the "JUNK" in
  3146. c     3 places) so that I won't be fooled by something that looks like
  3147. c     that kind of stuff.  Further, the security-code, file-type, -size,
  3148. c     and -record-length fields are also dummies ("IJ" in 4 places).
  3149.  
  3150.       call FmpParsePath(pdata,junk,name,typx,junk,ij,ij,ij,ij,junk)
  3151.  
  3152.       call CheckName(name)                !Replace illegals in the name
  3153.       if (rtpx .ne. ' ') then             !Replace type-extension?
  3154.           typx = rtpx                     !Yes - do it
  3155.       else
  3156.           call CheckName(typx)            !Replace illegals in type-extension
  3157.       endif
  3158.  
  3159. c     Now reconstruct the name.  If anything beyond a file-name was given in
  3160. c     a GET or RECEIVE command, it was parsed above; it will now be used
  3161. c     here to build a full file-name.
  3162.  
  3163.       call FmpBuildPath(fiNm,mask,name,typx,' ',sc,typ,fiSz,rl,ds)
  3164.  
  3165.       return
  3166.       end
  3167.  
  3168.       subroutine CheckName(name)                         ,<890525.1144>
  3169.      >Make file-name info legal
  3170.       implicit none
  3171.  
  3172.       character*(*) name
  3173.       integer*2     i,TrimLen
  3174.       character*1   b
  3175.  
  3176. c     This routine checks file-name and -extensions to insure that all
  3177. c     occurrences of '+', '-', ',', '@', leading numeric characters, and
  3178. c     embedded blanks or control-characters are replaced by '!'.
  3179.  
  3180.       do i = 1,TrimLen(name)              !Scan the file name
  3181.           b = name(i:i)                   !Extract a character
  3182.           if (i .eq. 1) then              !Check for numeric 1st character
  3183.               if (b.ge.'0' .and. b.le.'9') then
  3184.                   b = '!'
  3185.               endif
  3186.           else if (b .eq. '+') then
  3187.               b = '!'
  3188.           else if (b .eq. '-') then
  3189.               b = '!'
  3190.           else if (b .eq. '@') then
  3191.               b = '!'
  3192.           else if (b .eq. ',') then
  3193.               b = '!'
  3194.           else if (b .le. ' ') then
  3195.               b = '!'
  3196.           endif
  3197.           name(i:i) = b                   !Replace (possibly) changed byte
  3198.       end do
  3199.  
  3200.       return
  3201.       end
  3202.  
  3203.       character*1 function sinit(retry)                  ,<890525.1144>
  3204.      >Send initial packet
  3205.       implicit none
  3206.  
  3207.       include kercom.ftni,NOLIST
  3208.       include kerfil.ftni,NOLIST
  3209.       integer*2 retry,num,len,FmpOpen,err
  3210.       logical*2 fRtryi
  3211.       character*1 ptype,RecPack
  3212.       character*4 openopts
  3213.  
  3214.       if ( fRtryi(retry) ) then           !Retry limit exceeded?
  3215.           sinit = '!'                     !Yes - abort the send
  3216.           call SendAbort                  !Close all associated files
  3217.           return
  3218.       endif
  3219.  
  3220.       sCheck = 1                          !SINIT always uses 1-byte checks
  3221.       call SndPar('S',seq)                !Send my parameters
  3222.       ptype = RecPack(len,num)            !Get the response
  3223.       sinit = state                       !Assume no change in state
  3224.  
  3225.       if (ptype .eq. 'N') then            !NAK?
  3226.           return                          !Yes - try again
  3227.       else if (ptype .eq. 'T') then       !Time-out?
  3228.           return                          !try again
  3229.  
  3230.       else if (ptype .eq. 'E') then       !Error packet?
  3231.           ErrMsg = 'Receiver error: ' // RecPkt(5:len+4)
  3232.           sinit = 'E'
  3233.           call SendAbort
  3234.           return
  3235.  
  3236.       else if (ptype .eq. 'Y') then       !ACK?
  3237.           if (seq .ne. num) then          !Yes - for this packet?
  3238.               return                      !No - try again
  3239.           endif
  3240.           call RecPar(len)                !Get partner's parameters
  3241.           sCheck = NewChk                 !Ok to do new checksum type now
  3242.           if (fBnry .and. Parity.ne.3 .and. .not.fBit8) then
  3243.               ErrMsg = 'Can''t send binary file (parity problem)'
  3244.               fBnry = .false.
  3245.               sinit = 'E'
  3246.               call SendAbort
  3247.               return
  3248.           endif
  3249.           retry = 0                       !Clear the retry counter
  3250.           seq = mod(seq+1,64)             !Get next sequence number
  3251.           if (fBnry) then
  3252.               openopts = 'rofx'
  3253.           else
  3254.               openopts = 'ro'
  3255.           endif
  3256.           if (FmpOpen(fiCb,err,fiNm,openopts,1).lt.1) then
  3257.               call ReportFileError(err,fiNm)
  3258.           else
  3259.               fiPt = 0                    !Force a record to be read
  3260.               sinit = 'F'                 !Go to File-name state
  3261.           endif
  3262.  
  3263.       else if (ptype .ne. 'X') then       !Any response except checksum err?
  3264.           ErrMsg = 'Unknown packet type: ' // ptype
  3265.           sinit = '!'                     !Yes - abort the send
  3266.           call SendAbort
  3267.       endif
  3268.  
  3269.       return
  3270.       end
  3271.  
  3272.       character*1 function sfile(retry)                  ,<890525.1144>
  3273.      >Send file-name packet
  3274.       implicit none
  3275.  
  3276.       include kercom.ftni,NOLIST
  3277.       include kerfil.ftni,NOLIST
  3278.       integer*2 retry,len,num,TrimLen,j
  3279.       logical*2 fRetry
  3280.       character*1 ptype,RecPack
  3281.       character name*16,typex*4,cj*1
  3282.  
  3283.       if ( fRetry(retry) ) then           !Exceeded retry limit?
  3284.           sfile = '!'                     !Yes - abort the transfer
  3285.           call SendAbort                  !Close all files
  3286.           return
  3287.       endif
  3288.  
  3289. c     Get the "normal form" of the file-name.  The "J" and "CJ" variables
  3290. c     are "junk" (unused fields) from the parse.
  3291.  
  3292.       call FmpParsePath(fiNm,cj,name,typex,cj,j,j,j,j,cj)
  3293.       j = TrimLen(name)
  3294.       if (typex .ne. ' ') then            !Was a type-extension given?
  3295.           pdata = name(:j) // '.' // typex
  3296.       else
  3297.           pdata = name
  3298.       endif
  3299.       call SndPack('F',seq,TrimLen(pdata))  !Send the file name
  3300.       ptype = RecPack(len,num)            !Get the response
  3301.       sfile = state                       !Assume no change in state
  3302.  
  3303.       if (ptype .eq. 'T') then            !Time-out?
  3304.           return                          !just retry
  3305.  
  3306.       else if (ptype .eq. 'E') then       !Error packet?
  3307.           ErrMsg = 'Receiver error: ' // RecPkt(5:len+4)
  3308.           sfile = 'E'
  3309.           call SendAbort
  3310.           return
  3311.  
  3312.       else if (ptype .eq. 'N') then       !NAK or time-out?
  3313.           if (mod(seq+1,64) .ne. num) then
  3314.               return
  3315.           else
  3316.               ptype = 'Y'
  3317.               num = num - 1
  3318.           endif
  3319.       endif
  3320.  
  3321.       if (ptype .eq. 'Y') then            !ACK?
  3322.           if (seq .ne. num) then
  3323.               return
  3324.           endif
  3325.           retry = 0
  3326.           seq = mod(seq+1,64)             !Get next sequence number
  3327.           sfile = 'D'                     !Go to the data state
  3328.           if (R.ne.L) then                !Display the file name locally
  3329.               call tpCh('Sending -',fiNm,TrimLen(fiNm))
  3330.           endif
  3331.           call buffill                    !Get first data packet's worth
  3332.  
  3333.       else if (ptype .ne. 'X') then       !Anything else but checksum err...
  3334.           ErrMsg = 'Unknown packet type: ' // ptype
  3335.           sfile = '!'                     !...makes us abort the send
  3336.           call SendAbort
  3337.       endif
  3338.  
  3339.       return
  3340.       end
  3341.  
  3342.       character*1 function sdata(retry)                  ,<890525.1144>
  3343.      >Send file-data packets
  3344.       implicit none
  3345.  
  3346.       include kercom.ftni,NOLIST
  3347.       integer*2 retry,len,num,buffill
  3348.       logical*2 fRetry
  3349.       character*1 ptype,RecPack
  3350.  
  3351.       if ( fRetry(retry) ) then           !Exceeded retry limit?
  3352.           sdata = '!'                     !Yes - give up
  3353.           call SendAbort
  3354.           return
  3355.       endif
  3356.  
  3357.       sdata = state                       !Assume no change in state
  3358.       call SndPack('D',seq,slen)          !Send the data packet
  3359.  
  3360.       ptype = RecPack(len,num)            !Get the reply
  3361.  
  3362.       if (ptype .eq. 'T') then            !Time-out?
  3363.           return                          !Just retry
  3364.  
  3365.       else if (ptype .eq. 'E') then       !Error packet?
  3366.           ErrMsg = 'Receiver error: ' // RecPkt(5:len+4)
  3367.           sdata = 'E'
  3368.           call SendAbort
  3369.           return
  3370.  
  3371.       else if (ptype .eq. 'N') then       !Got a NAK?
  3372.           if (mod(seq+1,64) .eq. num) then
  3373.               return
  3374.           else
  3375.               ptype = 'Y'                 !A NAK on the 'next' packet is
  3376.               num = num - 1               !...an ACK on this one
  3377.           endif
  3378.       endif
  3379.  
  3380.       if (ptype .eq. 'Y') then            !Got an ACK?
  3381.           if (seq .ne. num) return
  3382.           retry = 0
  3383.           seq = mod(seq+1,64)
  3384.           if (buffill() .lt. 1) then      !Did we get EOF?
  3385.               sdata = 'Z'                 !Yes - change state
  3386.           endif
  3387.  
  3388.       else if (ptype .ne. 'X') then       !Something besides checksum?
  3389.           ErrMsg = 'Unknown packet type: ' // ptype
  3390.           sdata = '!'                     !Abort the transfer
  3391.           call SendAbort
  3392.       endif
  3393.  
  3394.       return
  3395.       end
  3396.  
  3397.       character*1 function sbreak(retry)                 ,<890525.1144>
  3398.      >Send EOT packet
  3399.       implicit none
  3400.  
  3401.       include kercom.ftni,NOLIST
  3402.       integer*2 retry,len,num
  3403.       logical*2 fRetry
  3404.       character*1 ptype,RecPack
  3405.  
  3406.       if ( fRetry(retry) ) then           !Did we exceed the retry limit
  3407.           sbreak = '!'                    !Yes - abort the transfer
  3408.           call SendAbort
  3409.           return
  3410.       endif
  3411.  
  3412.       sbreak = state                      !Assume no state change
  3413.       call SndPack('B',seq,0)
  3414.       ptype = RecPack(len,num)
  3415.  
  3416.       if (ptype .eq. 'T') then            !timed out?
  3417.           return                          !just retry
  3418.  
  3419.       else if (ptype .eq. 'E') then       !Error packet?
  3420.           ErrMsg = 'Receiver error: ' // RecPkt(5:len+4)
  3421.           sbreak = 'E'
  3422.           call SendAbort
  3423.           return
  3424.  
  3425.       else if (ptype .eq. 'N') then       !Were we NAKed?
  3426.           if (mod(seq+1,64).ne.num) then  !Yes - is it an old NAK?
  3427.               return                      !No - do another break
  3428.           else
  3429.               ptype = 'Y'
  3430.               num = num - 1
  3431.           endif
  3432.       endif
  3433.  
  3434.       if (ptype .eq. 'Y') then            !Were we ACKed?
  3435.           if (num .ne. seq) then          !Yes - in sequence?
  3436.               return                      !No - do the break again
  3437.           endif
  3438.           retry = 0
  3439.           seq = mod(seq+1,64)
  3440.           sbreak = 'C'                    !Change to Complete status
  3441.  
  3442.       else if (ptype .ne. 'X') then       !Anything else but checksum?
  3443.           ErrMsg = 'Unknown packet type: ' // ptype
  3444.           sbreak = '!'                    !Yes - abort the send
  3445.           call SendAbort
  3446.       endif
  3447.  
  3448.       return
  3449.       end
  3450.  
  3451.       integer*2 function buffill()                       ,<890525.1144>
  3452.      >Fill transmit buffer
  3453.       implicit none
  3454.  
  3455. c     This routine copies data from the sending disc file to the data
  3456. c     portion of the transmit packet.  It is responsible for assuring
  3457. c     that control, 8th-bit, and repeat-count prefixing sequences are
  3458. c     not broken across a packet boundary.
  3459.  
  3460.       include kercom.ftni,NOLIST
  3461.       integer*2 i,j,b,dgetc,ctl,psave,DataMax,TrimLen,xbufL
  3462.       character*20 xBuf                   !"excess" buffer
  3463.       data xBuf /' '/                     !(SegLd always clears this)
  3464.  
  3465.       p = 1                               !Reset packet byte pointer
  3466.       DataMax = sPkSiz - (sCheck+1)       !(This is last available byte + 1)
  3467.       if (xBuf .ne. ' ') then             !Anything in the overflow buffer?
  3468.           pData = xBuf                    !Yes - put it in front of packet
  3469.           p = xBufL + 1                   !...and reset pointer to end of it
  3470.           xBuf = ' '                      !(show overflow buffer is clear)
  3471.       endif
  3472.       do while (dgetc(b) .ge. 0)          !Read from disc 'til EOF
  3473.  
  3474.           psave = p                       !In case of buffer overflow
  3475.           if (fRepc) then                 !Can we do repeat-counts?
  3476.               i = 1                       !Yes - find non-match/too many
  3477.               do while (dgetc(j).eq.b .and. i.lt.94)
  3478.                   i = i + 1               !Max repeat-count is 94
  3479.               end do
  3480.               call dPutBack(j)            !Put back the non-match
  3481.               if (i .lt. 4) then          !Below the repeat threshold?
  3482.                   do while (i .gt. 1)     !Yes - put 'em all back
  3483.                       call dPutBack(b)
  3484.                       i = i - 1
  3485.                   end do
  3486.               else
  3487.                   call pPutc(sRepc)       !Yes - plug in the prefix
  3488.                   call pPutc(i + 32)      !Make the count printable
  3489.               endif
  3490.           endif
  3491.  
  3492.           if (fBit8 .and. b.gt.127) then  !Do 8th-bit prefixing?
  3493.               call pPutc(sBit8)           !Yes - output the prefix
  3494.               b = iand(b,127)             !...then clear 8th bit
  3495.           endif
  3496.  
  3497.           if (b.lt.32 .or. b.eq.127 .or. b.eq.sQUOTE .or.
  3498.      >                      (b.eq.sBit8 .and. fBit8) .or.
  3499.      >                      (b.eq.sRepc .and. fRepc)      ) then
  3500.               call pPutc(sQUOTE)          !We need to quote a character
  3501.               if (b.lt.32 .or. b.eq.127) b = ctl(b)
  3502.           endif
  3503.           call pPutc(b)
  3504.  
  3505.           if (p .eq. DataMax) then        !Packet exactly filled?
  3506.               goto 10
  3507.           elseif (p .gt. DataMax) then    !Packet too full?
  3508.               xBuf = pData(psave:p)       !Save part that doesn't fit
  3509.               xbufL = p - psave           !Save length of the overflow
  3510.               p = psave                   !Back up to last char that fits
  3511.               goto 10                     !Done with this packet
  3512.           endif
  3513.       end do
  3514.  
  3515. c     Falling thru to here means we hit EOF in the disc file, or the packet
  3516. c     is full.  If at EOF and this routine is called again, it will return
  3517. c     zero, and SDATA will know to go to EOF state.
  3518.  
  3519.  10   slen = p - 1
  3520.       buffill = slen
  3521.  
  3522.       return
  3523.       end
  3524.  
  3525.       integer*2 function dGetc(b)                        ,<890525.1144>
  3526.      >Get a disc-file byte
  3527.       implicit none
  3528.  
  3529. c     The RTE file-system doesn't readily accept the notion that a file
  3530. c     could be a "stream" of bytes.  This subroutine fills that gap by
  3531. c     doing record unpacking and a limited "push-back" facility.
  3532.  
  3533.       include kercom.ftni,NOLIST
  3534.       include kerfil.ftni,NOLIST
  3535.       include kerdbg.ftni,NOLIST
  3536.  
  3537.       integer*2 dPutBack,b,pbbyt
  3538.       logical*2 EORpend
  3539.       integer*2 pbpt,err,fiBf(128)
  3540.       integer*2 FmpRead
  3541.       character pbbf*20                   !Push-back buffer
  3542.       equivalence (fiCh,fiBf)
  3543.       data pbpt /0/                       !if > 0, is a push-back pointer
  3544.       data EORpend /.false./              !EOR sequence is not pending
  3545.  
  3546.       if (pbpt .gt. 0) then               !Was a byte pushed back?
  3547.           b = ichar(pbbf(pbpt:pbpt))      !Yes - get it
  3548.           pbpt = pbpt - 1                 !Back up the push-back pointer
  3549.           goto 20
  3550.       endif
  3551.  
  3552.       if (EORpend) then                   !Doing 2nd byte of EOR sequence?
  3553.           EORpend = .false.               !Not any more
  3554.           b = 10                          !Return the LF character
  3555.           goto 20
  3556.       endif
  3557.  
  3558.  10   if (fiPt .lt. 1) then               !Need to read a record?
  3559.           fiLn = FmpRead(fiCb,err,fiBf,MAXREC)   !Yes - do it
  3560.           if (err .lt. 0) then                   !File-read error?
  3561.               if (fBnry .and. err.eq.-12) then   !EOF during binary mode?
  3562.                   fiLn = -1                      !Yes - show EOF
  3563.               else
  3564.                   call ReportFileError(err,fiNm) !Else report other errors
  3565.               endif
  3566.           endif
  3567.           fiPt = 1                               !Point to 1st byte
  3568.       endif
  3569.  
  3570.       if (fiLn .lt. 0) then               !EOF? (always returns -1)
  3571.           b = -1                          !NOTE: EOF can't be pushed back!
  3572.           goto 20
  3573.       endif
  3574.  
  3575.       if (fiPt .gt. fiLn) then            !EOR?
  3576.           fiPt = 0                        !Yes - Arrange to get next record
  3577.           if (fBnry) goto 10              !Don't map EOR to CRLF if binary
  3578.           b = 13                          !Else flag with a CR
  3579.           EORpend = .true.                !Be sure to do 2nd byte of EOR
  3580.       else
  3581.           b = ichar( fiCh(fiPt:fiPt) )    !Get the current byte
  3582.           fiPt = fiPt + 1                 !Point to next byte
  3583.       endif
  3584.  
  3585.  20   dGetc = b                           !Return byte as function value
  3586.       return
  3587.  
  3588.       entry dPutBack(pbbyt)
  3589.  
  3590.       if (pbpt .ge. 20) then              !Too many pushed back?
  3591.           ErrMsg = 'Too many bytes pushed back!'
  3592.           call kDebug(all,ErrMsg,' ')
  3593.           call SndErr
  3594.           call quit
  3595.       else if (pbbyt .ge. 0) then         !(Don't push EOF back!)
  3596.           pbpt = pbpt + 1                 !Bump the pointer
  3597.           pbbf(pbpt:pbpt) = char(pbbyt)   !"push back" the byte
  3598.       endif
  3599.  
  3600.       return
  3601.       end
  3602.  
  3603.       subroutine BufEmp(len)                             ,<890525.1144>
  3604.      >Empty receive-buffer
  3605.       implicit none
  3606.  
  3607. c     This routine writes the packet buffer contents to the receiving
  3608. c     disc file.  Note that the "LEN" parameter is actually a pointer
  3609. c     to the last data-byte in the packet (type-2/-3 checksums???)
  3610.  
  3611.       include kercom.ftni,NOLIST
  3612.  
  3613.       integer*2 ctl,i,len,b
  3614.       logical*2 f8Set
  3615.       integer*2 rep,j
  3616.  
  3617.       i = 5                               !1st data byte is packet byte #5
  3618.       do while (i .le. len+4)             !put (len) bytes in disc file
  3619.           b = ichar(R1cPkt(i))            !Get next packet byte
  3620.  
  3621.           if (fRepc .and. b.eq.Repc) then !Is this my repeat-count char?
  3622.               i = i + 1                   !Yes - bump to the count itself
  3623.               rep = ichar(R1cPkt(i))-32   !Get the count
  3624.               i = i + 1                   !position to next byte
  3625.               b = ichar(R1cPkt(i))        !Get character to repeat
  3626.           else
  3627.               rep = 1                     !Default repeat-count to 1
  3628.           endif
  3629.  
  3630.           if (fBit8 .and. b.eq.Bit8) then !Do 8th-bit prefixing?
  3631.               f8Set = .true.              !Yes - set the bit-8 flag
  3632.               i = i + 1                   !...and bump the byte pointer
  3633.               b = ichar(R1cPkt(i))
  3634.           else
  3635.               f8Set = .false.
  3636.           endif
  3637.  
  3638.           if (b .eq. Quote) then          !If this is my quote character
  3639.               i = i + 1                   !...bump the byte pointer
  3640.               b = ichar(R1cPkt(i))        !...and get the next byte
  3641.               if (b.ge.63 .and. b.le.95) then  !if in range...
  3642.                   b = ctl(b)              !...de-controllify it (else it is
  3643.               endif                       !sent literally after a quote)
  3644.           endif
  3645.  
  3646.           if (f8Set) b = ior(b,200b)      !Turn on 8th bit as needed
  3647.  
  3648.           do j = 1,rep                    !Repeat-count processing
  3649.               call dPutc(b)               !Put the byte in the file
  3650.           end do
  3651.           i = i + 1
  3652.       end do
  3653.       return
  3654.       end
  3655.  
  3656.       subroutine dPutc(b)                                ,<890525.1144>
  3657.      >Write byte in file-buffer
  3658.       implicit none
  3659.  
  3660.       include kerfil.ftni,NOLIST
  3661.  
  3662.       integer*2 b,err,fiBf(129)
  3663.       logical*2 CRpend                    !True if CR is pending
  3664.       logical*2 fEmpty                    !True if file-buffer is empty
  3665.       equivalence (fiCh,fiBf)
  3666.       data CRpend /.false./
  3667.  
  3668. c     This routine is called with a single INTEGER parameter (b) being the
  3669. c     ichar() of the byte to store into the record, except:
  3670. c         b = -2: post the current (non-empty) record
  3671. c         b = -1: post the current record, empty or not
  3672. c     If b is any other negative number, it is not written to the file.
  3673.  
  3674.       if (b.eq.-2 .and. fEmpty) return    !This was an un-needed post call
  3675.       fEmpty = .false.
  3676.  
  3677.       if (fBnry) goto 10                  !Don't map CRLF to EOR if binary
  3678.  
  3679.       if (CRpend) then                    !Is a CR pending?
  3680.           CRpend = .false.                !Yes - not after this stuff
  3681.           if (b.eq.10 .or. b.lt.0) then   !Is this the logical EOR?
  3682.               b = -1                      !Yes - flag it
  3683.           else
  3684.               fiPt = fiPt + 1             !Not EOR, so store the CR
  3685.               fiCh(fiPt:fiPt) = char(13)  !...into the file
  3686.           endif
  3687.       endif
  3688.  
  3689.       if (b .eq. 13) then                 !Is this a CR?
  3690.           CRpend = .true.                 !Yes - flag it for next call
  3691.           return                          !Don't write it to the file (yet)
  3692.       endif
  3693.  
  3694. !     Do end-of-record (EOR) processing if requested (b=-1) or if the
  3695. !     record-buffer gets "full".
  3696.  
  3697.  10   if (fiPt.ge.MAXREC .or.  b.lt.0) then
  3698.           call FmpWrite(fiCb,err,fiBf,fiPt)
  3699.           if (err .lt. 0) then
  3700.               call ReportFileError(err,fiNm)
  3701.           endif
  3702.           fiCh = ' '                      !Clear the record buffer
  3703.           fiPt = 0                        !Reset char pointer
  3704.           fEmpty = .true.                 !"buffer is empty"
  3705.       endif
  3706.  
  3707.       if (b .ge. 0) then                  !Add this byte to the record?
  3708.           fiPt = fiPt + 1                 !Yes - bump the pointer
  3709.           fiCh(fiPt:fiPt) = char(b)       !...and add the byte
  3710.       endif
  3711.  
  3712.       return
  3713.       end
  3714.  
  3715.       logical*2 function fRetry(try)                     ,<890525.1144>
  3716.      >OK to retry?
  3717.       implicit none
  3718.  
  3719.       include kercom.ftni,NOLIST
  3720.       integer*2 try,tmax
  3721.       logical*2 IfBrk,fInit,tf
  3722.       logical*2 fRtryI                    !Alternate entry for initial retry
  3723.  
  3724.       tmax = MaxTry                       !Set for "normal" retry-limit
  3725.       fInit = .false.                     !Not doing initial retry limiting
  3726.       goto 10
  3727.  
  3728.       entry fRtryi(try)
  3729.       tmax = ImxTry                       !Set for initial retry limit
  3730.       fInit = .true.                      !Doing initial retry-limiting
  3731.  
  3732.  10   tf = .true.                         !Intialize to 'not ok to retry'
  3733.       if (try .gt. tMax) then
  3734.           ErrMsg = 'Retry limit exceeded'
  3735.           if (R .ne. L) call tpFm(ErrMsg)
  3736.       else
  3737.           try = try + 1
  3738.           tf = .false.
  3739.       endif
  3740.  
  3741.       If (IfBrk()) then                   !Operator break?
  3742.           tf = .true.
  3743.           ErrMsg = 'Operator break'
  3744.           if (R .ne. L) call tpFm(ErrMsg)
  3745.       endif
  3746.  
  3747.       if ( fInit ) then                   !Doing Initial or normal?
  3748.           fRtryi = tf
  3749.       else
  3750.           fRetry = tf
  3751.       endif
  3752.  
  3753.       return
  3754.       end
  3755.  
  3756.       character*1 function RecPack(len,num)              ,<890525.1144>
  3757.      >Read a packet
  3758.       implicit none
  3759.  
  3760.       include kercom.ftni,NOLIST
  3761.       include kerdbg.ftni,NOLIST
  3762.       include kersta.ftni,NOLIST
  3763.  
  3764.       integer*2 len,num
  3765.       integer*2 unchar,getpak
  3766.       integer*2 RecBuf,i,b,ck,type,numw1(3),numw2(3),check1
  3767.       character num1*2,num2*2,work*10,rpinfo*256
  3768.       equivalence (RecBuf,RecPkt),(num1,numw1(3)),(num2,numw2(3))
  3769.  
  3770. c     By definition, anything I read in will terminate with EOLch (a
  3771. c     carriage-return), so all I need to do is left-justify the SYNC
  3772. c     portion in the packet-buffer.  Packet input is always from the
  3773. c     REMOTE lu.  The rest of the routine "parses" the packet into
  3774. c     fields by setting appropriate variables.
  3775. c     RECPACK returns the packet-type as its value, or a 'bad-packet'
  3776. c     indicator if the checksum is bad.  The data length is returned
  3777. c     in LEN, and the sequence number is returned in NUM.
  3778.  
  3779. c     NOTE: it is assumed that type-ahead has been enabled prior to
  3780. c           calling this routine.  RTE's internal I/O processing
  3781. c           makes "true" full-duplex impossible (see the code in
  3782. c           CONNECT for proof of this); type-ahead on the mux card
  3783. c           at least allows us to capture data sent to us before we
  3784. c           are actually ready to receive it.  (While testing KERMIT
  3785. c           with two computers of different execution speed, data
  3786. c           losses DID occur because of the time needed to do stuff
  3787. c           between the sending of a packet and receipt of the ACK
  3788. c           or NAK.)
  3789.  
  3790.  5    i = getpak()                        !Do system-dependent packet-read
  3791.  
  3792.       if ( btest(i,rtoBit) ) then         !Did we time-out?
  3793.           recpack = 'T'                   !Give'em a time-out
  3794.           return
  3795.       endif
  3796.       if (rlen .lt. 1) goto 5             !Must have been just a <CR>
  3797.       rbytes = rbytes + rlen              !Count bytes
  3798.       rovrhd = rovrhd + rlen              !all are overhead for now
  3799.       RecPkt(rlen+1:) = ' '               !Clear unused part of packet
  3800.       call IBMrd                          !look for IBM-PROMPT as needed
  3801.       call kdebug(packets,'RecPack: ',RecPkt(:rlen))
  3802.       rpak = rpak + 1                     !Count a received packet
  3803.       p = index(RecPkt,char(Sync))        !Find the sync character
  3804.       if (p .lt. 1) goto 5                !No sync mark; retry read
  3805.  
  3806.  10   if (p .lt. 1) then                  !No sync found?
  3807.           RecPack = 'T'                   !Act as if it timed out
  3808.           return
  3809.       endif
  3810.       if (p .gt. 1) then                  !Left-justify the sync byte
  3811.           rpinfo = RecPkt(p:)             !(Ftn7x can't assign substring to
  3812.           RecPkt = rpinfo                 !  a variable of the same name)
  3813.           rlen = rlen - (p - 1)           !Adjust the packet-length
  3814.           p = 1                           !Reset the pointer to the mark
  3815.       endif
  3816.       len  = -1                           !Clear the field variables to
  3817.       num  = -1                           !  allow each field to be picked
  3818.       type = -1                           !  off in the correct order
  3819.       i = 0                               !Clear the data-byte counter
  3820.  
  3821.       do while (p .le. rlen)              !Scan the packet
  3822.           p = p + 1                       !Go to next byte
  3823.           b = ichar(R1cPkt(p))            !...and get it
  3824.  
  3825.           if (b .eq. Sync) then           !Re-sync?
  3826.               goto 10
  3827.  
  3828.           else if (len .lt. 0) then       !Do data-length field
  3829.               len = unchar(b)-2-sCheck    !...set data length
  3830.  
  3831.           else if (num .lt. 0) then       !Do sequence-number field
  3832.               num = unchar(b)
  3833.  
  3834.           else if (type .lt. 0) then      !Do packet-type field
  3835.               type = b
  3836.  
  3837.           else if (i .lt. len) then       !Do packet-data field (if any)
  3838.               i = i + 1                   !Count a data byte
  3839.  
  3840.           else                            !Do checksum field
  3841.               ck = check1(R1cPkt,p)       !Compute the checksum
  3842.               b = unchar(b)
  3843.               if (ck .ne. b) then         !bad checksum?
  3844.                   recpack = 'X'           !...report it to sender
  3845.                   call cnumd(ck,numw1)    !computed cksm --> ASCII
  3846.                   call cnumd(b,numw2)     !actual cksm --> ASCII
  3847.                   work = num1 // ', got ' // num2
  3848.                   call kdebug(packets,'Bad Checksum, needed ',work)
  3849.               else
  3850.                   recpack = char(type)
  3851.               endif
  3852.               rlen = p                    !Note the end of the packet
  3853.               rovrhd = rovrhd - len       !Adjust overhead for data count
  3854.               return
  3855.           endif
  3856.  
  3857.       end do
  3858.  
  3859.       ErrMsg = 'Illegally formed packet'
  3860.       recpack = '!'            !!!!If we got here, we ran out of data
  3861.  
  3862.       return
  3863.       end
  3864.  
  3865.       integer*2 function ctl(b)                          ,<890525.1144>
  3866.      >Make controls printable
  3867.       implicit none
  3868.  
  3869. c     Toggle the 7th bit of a byte such that CTRL-A <--> A
  3870.  
  3871.       integer*2 b
  3872.  
  3873.       ctl = ixor(b,64)
  3874.  
  3875.       return
  3876.       end
  3877.  
  3878.       subroutine pPutc(b)                                ,<890525.1144>
  3879.      >Store a packet-data byte
  3880.       implicit none
  3881.  
  3882.       include kercom.ftni,NOLIST
  3883.       integer*2 b
  3884.  
  3885.       p1ata(p) = char(b)                  !Store the byte
  3886.       p = p + 1                           !...and count it
  3887.  
  3888.       return
  3889.       end
  3890.  
  3891.       subroutine recpar(len)                             ,<890525.1144>
  3892.      >Save other's parameters
  3893.       implicit none
  3894.  
  3895.       include kercom.ftni,NOLIST
  3896.       include kercnf.ftni,NOLIST          !Defines fRmx
  3897.       integer*2 len
  3898.       integer*2 pblocksize
  3899.       parameter (pblocksize = 9)
  3900.       integer*2 sdefs(pblocksize)         !Remote default parameters
  3901.       integer*2 unchar,ctl,pgetc,i,b,maxp
  3902.  
  3903.       Data sdefs /                        !Set default remote parameters
  3904.      >            80,                     !Maximum packet = 80 bytes
  3905.      >            30,                     !receive timeout = 30 seconds
  3906.      >            0,                      !no padding required
  3907.      >            0,                      !(padding uses nulls)
  3908.      >            13,                     !<CR> terminates a packet
  3909.      >            35,                     !Control-quoting uses "#"
  3910.      >            32,                     !No 8th-bit prefixing will be done
  3911.      >            1,                      !1-byte checksums
  3912.      >            32/                     !No repeat-counts will be done
  3913.  
  3914.       call MoveWords(sdefs,sPkSiz,pblocksize) !Set remote defaults
  3915.       call Set_Timeout(R,sTime*100,fRmx)  !Preset remote timeout
  3916.       NewChk = 1                          !Reset default checksum type
  3917.       p = 5                               !Start with the MAXL field
  3918.       fBit8 = .false.                     !No 8th-bit stuff unless agreed
  3919.       fRepC = .false.                     !No repeat-count unless agreed
  3920.       maxp = rlen - 1                     !Get last param pos (w/ checkt=1)
  3921.  
  3922.       do i = 1,pblocksize                 !parse each parameter field
  3923.           if (p .gt. maxp) then           !Past end of received data?
  3924.               sPcnt = i - 1               !Save # of parameters passed
  3925.               return
  3926.           endif
  3927.           b = ichar(R1cPkt(p))            !Get a parameter byte
  3928.           if (b .eq. 0) return            !Done?
  3929.           p = p + 1                       !bump the byte pointer
  3930.  
  3931.           if (i .eq. 1) then              !Packet size?
  3932.               sPkSiz = min(94,unchar(b))
  3933.           else if (i .eq. 2) then         !Timeout value?
  3934.               b = unchar(b)               !Yes - get it as usual
  3935.               sTime = b                   !Copy it to parameter block
  3936.               call Set_timeout(R,b*100,fRmx) !...and set the timeout up
  3937.           else if (i .eq. 3) then
  3938.               sPad = unchar(b)
  3939.           else if (i .eq. 4) then         !Is it the pad character?
  3940.               sPadch = ctl(b)
  3941.           else if (i .eq. 5) then         !EOL character?
  3942.               sEOL = unchar(b)
  3943.           else if (i.eq.6) then           !Quote character?
  3944.               sQuote = b
  3945.           else if (i.eq.7) then           !8th-bit prefix character?
  3946.               call valBquote(b)
  3947.           else if (i.eq.8) then           !Checksum type?
  3948.               if (b.gt.48 .and. b.lt.52) NewChk = b-48   !Allow only '1'-'3'
  3949.               ChkTyp = NewChk    !>>>1.98c I'll use that checksum-type too
  3950.           else if (i.eq.9) then           !Repeat-character prefix?
  3951.               sRepc = b                   !Yes - save it
  3952.               if (b.eq.Repc .and. b.gt.32) then !If same as mine & not blank
  3953.                   fRepc = .true.          !...then we'll do it
  3954.               endif
  3955.           endif
  3956.       end do
  3957.  
  3958.       return
  3959.       end
  3960.  
  3961.       subroutine SndPar(ptype,num)                       ,<890525.1144>
  3962.      >Send my parameters
  3963.       implicit none
  3964.  
  3965.       include kercom.ftni,NOLIST
  3966.       integer*2 num
  3967.       integer*2 i,ctl,toChar,svCheck
  3968.       character*1 ptype
  3969.  
  3970.       toChar(i) = i+32                    !Raise controls to printable
  3971.  
  3972.       svCheck = sCheck                    !Save partner's checksum type
  3973.       sCheck = 1                          !Always send params w/ 1-byte check
  3974.       p = 1                               !Reset the packet pointer
  3975.       pdata = ' '                         !Remove the old data
  3976.       call pPutc(toChar(PakSiz))          !Send my current packet size
  3977.       call pPutc(toChar(Timeout))         !Set my timeout in
  3978.       call pPutc(toChar(nPad))            !I don't need padding
  3979.       call pPutc(ctl(Padch))              !(and my pad-character is NULL)
  3980.       call pPutc(toChar(EOLch))           !My EOL is always 13
  3981.       call pPutc(Quote)                   !Give 'em my quote character
  3982.       call pPutc(Bit8)                    !...and my 8th-bit prefix
  3983.       call pPutc(ChkTyp + 48)             !Show which checksum type I want
  3984.       call pPutc(Repc)                    !Give 'em my repeat character
  3985.       i = min(sPcnt,9)                    !Don't send too many parameters
  3986.       call SndPack(ptype,num,i)           !Now send the packet
  3987.  
  3988.       sCheck = svCheck                    !Restore partner's checksum type
  3989.  
  3990.       return
  3991.       end
  3992.  
  3993.       integer*2 function unchar(ch)                      ,<890525.1144>
  3994.      >Undo "TOCHAR" operation
  3995.       implicit none
  3996.  
  3997.       integer*2 ch
  3998.  
  3999.       unchar = ch - 32
  4000.       return
  4001.       end
  4002.  
  4003.       subroutine fClose                                  ,<890525.1144>
  4004.      >Close current s/r file
  4005.       implicit none
  4006.  
  4007.       include kerfil.ftni,NOLIST
  4008.       integer*2 err
  4009.       integer*4 r,p
  4010.  
  4011.       if (fbnry) then                     !Need to trim a type-1 transfer?
  4012.           call FmpPosition(fiCb,err,r,p)  !Yes - where are we in the file?
  4013.           call FmpTruncate(fiCb,err,r-1)  !...truncate at one record less
  4014.       endif
  4015.       call FmpClose(fiCb,err)
  4016.  
  4017.       return
  4018.       end
  4019.  
  4020.       subroutine SendAbort                               ,<890525.1144>
  4021.      >Kill all send stuff
  4022.       implicit none
  4023.  
  4024.       include kercom.ftni,NOLIST
  4025.       include kerfil.ftni,NOLIST
  4026.       integer*2 err
  4027.  
  4028.       call fClose
  4029.       call FmpEndMask(maCb)
  4030.  
  4031. c -->  More to do???
  4032.  
  4033.       return
  4034.       end
  4035.  
  4036.       subroutine success(pass)                           ,<890525.1144>
  4037.      >Print pass/fail if local
  4038.       implicit none
  4039.  
  4040.       include kercom.ftni,NOLIST
  4041.       logical*2 pass
  4042.  
  4043.       if (R .ne. L) then                  !If we are local...
  4044.           call tpFm('_')                 !Sound the bell
  4045.           call sleep(100)
  4046.           if (pass) then
  4047.               call tpFm('File transfer(s) completed')
  4048.           else
  4049.               call tpFm('File transfer(s) failed')
  4050.           endif
  4051.           call sleep(100)
  4052.           call tpFm('')                  !Beep again
  4053.       endif
  4054.  
  4055.       return
  4056.       end
  4057.  
  4058.       subroutine startstats                              ,<890525.1144>
  4059.      >Start statistics logging
  4060.       implicit none
  4061.  
  4062.       include kersta.ftni,NOLIST
  4063.       integer*4 timenow
  4064.  
  4065. c     Clear all counters associated with the 'last transfer'
  4066.  
  4067.       spak = 0                            !Packets sent
  4068.       rpak = 0                            !Packets received
  4069.       rtry = 0                            !# of retries (sent or received)
  4070.       sbytes = 0                          !Bytes (total) sent
  4071.       rbytes = 0                          !...and recieved
  4072.       sovrhd = 0                          !Overhead bytes sent
  4073.       rovrhd = 0                          !...and received
  4074.  
  4075.       startim = timenow()                 !Start the clock
  4076.  
  4077.       return
  4078.       end
  4079.  
  4080.       subroutine endstats                                ,<890525.1144>
  4081.      >End statistics logging
  4082.       implicit none
  4083.  
  4084.       include kersta.ftni,NOLIST
  4085.       integer*4 timenow
  4086.  
  4087.       trpak = trpak + rpak                !Total the packets received
  4088.       tspak = tspak + spak                !Total the packets sent
  4089.       trtry = trtry + rtry                !Total the packets retried
  4090.  
  4091.       endtim = timenow()                  !Stop the clock
  4092.       if (endtim .lt. startim) then       !Did we cross midnight?
  4093.           endtim = endtim + 86400J        !Yes - add a day's seconds
  4094.       endif
  4095.  
  4096.       return
  4097.       end
  4098.  
  4099.       subroutine ValBQuote(b)                            ,<890525.1144>
  4100.      >Validate BQuote param
  4101.       implicit none
  4102.  
  4103.       include kercom.ftni,NOLIST
  4104.       integer*2 b,BIGY,AMPERSAND
  4105.       parameter (AMPERSAND = 38)
  4106.       parameter (BIGY      = 89)
  4107.  
  4108.       sBit8 = b                           !Save partner's 8-th bit character
  4109.       fBit8 = .false.                     !Preset BQuote processing off
  4110.       if (.not. f8OK) return              !Don't process if user has it off
  4111.  
  4112.       if (b .eq. BIGY) then               !Partner wants to use my byte?
  4113.           fBit8 = .true.                  !Yes - turn it on
  4114.           if (.not. fSend) then           !If I am to receive...
  4115.               Bit8 = AMPERSAND            !...need to use default bquote
  4116.           endif
  4117.           sBit8 = Bit8                    !Set sender's BQuote to mine
  4118.           return
  4119.       endif
  4120.  
  4121.       if ((b.gt.32 .and. b.lt.63) .or. (b.gt.95 .and. b.lt.127)) then
  4122.           Bit8 = b                        !Remote: "Do it with this byte"
  4123.           fbit8 = .true.                  !Flag the agreement
  4124.       endif
  4125.  
  4126.       return
  4127.       end
  4128.  
  4129.       subroutine doIpacket                               ,<890525.1144>
  4130.      >Process 'I' packet
  4131.       implicit none
  4132.  
  4133.       include kercom.ftni,NOLIST
  4134.       include kerfil.ftni,NOLIST          !To define fBnry
  4135.  
  4136. !     This routine's purpose is to perform all of those
  4137. !         "set param value"
  4138. !     on receipt of a server I (initialize) packet.  We are currently only
  4139. !     interested in the quote, bquote, and repeat characters, and the
  4140. !     checksum type.  Note that if b-quoting is disabled and parity isn't
  4141. !     none, we will not be able transfer binary, so if that flag is on,
  4142. !     turn it off!
  4143.  
  4144.       Quote = sQuote                      !Set myself to partner's quote
  4145.       Bit8 = sBit8                        !...and partner's bquote
  4146.       f8OK = Bit8 .ne. 32                 !Allow binary quoting?
  4147.       ChkTyp = sCheck                     !Partner wants special checksums?
  4148.       Repc = sRepc                        !Get partner's repeat character
  4149.  
  4150.       if (fBnry .and. Parity.ne.3 .and. .not.f8OK) fBnry = .false.
  4151.  
  4152.       return
  4153.       end
  4154.  
  4155.       character*1 function seof(retry)                   ,<890525.1144>
  4156.      >Send the EOF packet
  4157.       implicit none
  4158.  
  4159.       include kercom.ftni,NOLIST
  4160.       include kerfil.ftni,NOLIST
  4161.       integer*2 retry,num,len,err,FmpOpen
  4162.       logical*2 fRetry
  4163.       character*1 ptype,RecPack
  4164.       character*4 openopts
  4165.  
  4166.       if ( fRetry(retry) ) then
  4167.           seof = '!'
  4168.           call SendAbort
  4169.           return
  4170.       endif
  4171.  
  4172.       call SndPack('Z',seq,0)             !Send the EOF packet
  4173.       seof = state                        !Assume no change in state
  4174.       ptype = RecPack(len,num)            !Get the response
  4175.  
  4176.       if (ptype .eq. 'T') then            !Time-out?
  4177.           return                          !just retry
  4178.  
  4179.       else if (ptype .eq. 'E') then       !Error packet?
  4180.           ErrMsg = 'Receiver error: ' // RecPkt(5:len+4)
  4181.           seof = 'E'
  4182.           call SendAbort
  4183.           return
  4184.  
  4185.       else if (ptype .eq. 'N') then       !NAK?
  4186.           if (mod(seq+1,64) .ne. num) then
  4187.               return
  4188.           else
  4189.               ptype = 'Y'
  4190.               num = num - 1
  4191.           endif
  4192.       endif
  4193.  
  4194.       if (ptype .eq. 'Y') then            !ACK?
  4195.           if (num .ne. seq) return
  4196.           retry = 0
  4197.           call fClose                     !Close the current file
  4198.           seq = mod(seq+1,64)             !Set next sequence number
  4199.           seof = '@'
  4200.  
  4201.       else                                !Unknown packet type
  4202.           ErrMsg = 'Unknown packet type: ' // ptype
  4203.           seof = '!'                      !Abort the transfer
  4204.           call SendAbort                  !Close sending/search files
  4205.       endif
  4206.  
  4207.       return
  4208.       end
  4209.  
  4210.       subroutine SndErr                                  ,<890525.1144>
  4211.      >Send error-packet
  4212.       implicit none
  4213.  
  4214.       include kercom.ftni,NOLIST
  4215.       integer*2 len,TrimLen
  4216.  
  4217.       pdata = ErrMsg
  4218.       len = TrimLen(pdata)
  4219.       if (fPkIO) then
  4220.           call SndPack('E',seq,len)       !Send error-packet
  4221.           if (R .ne. L) call tpFm(ErrMsg) !Inform local user
  4222.       else
  4223.           call tpFm(ErrMsg)
  4224.       endif
  4225.  
  4226.       return
  4227.       end
  4228.  
  4229.       subroutine sndpack(type,num,len)                   ,<890525.1144>
  4230.      >Transmit a packet
  4231.       implicit none
  4232.  
  4233. c     NOTE: it is assumed that asynchronous interrupts on the remote
  4234. c           LU have been disabled prior to this routine.
  4235.  
  4236.       include kercom.ftni,NOLIST
  4237.       include kerdbg.ftni,NOLIST
  4238.       include kersta.ftni,NOLIST
  4239.  
  4240.       character*1 type
  4241.       integer*2 num,len
  4242.       integer*2 i,j,check1,check2,check3
  4243.       character*1 toChar
  4244.  
  4245.       toChar(i) = char(i+32)          !Raise controls to printable range
  4246.  
  4247.       i = sPad + len + 5 + sCheck     !Compute length of packet
  4248.       sbytes = sbytes + i             !Count 'em
  4249.       sovrhd = sovrhd + i - len       !Adjust overhead for data
  4250.  
  4251.       if (sPad .gt. 0) then           !Need to do padding?
  4252.           do i = 1,50                 !Yes - build a pad-character buffer
  4253.               P1cket(i) = char(sPadCh)
  4254.           end do
  4255.           do i = 1,sPad,50            !Send padding in 50-byte pieces
  4256.               j = min(50,sPad-(i-1))  !Set tx count (don't overdo padding!)
  4257.               call putpak(j)
  4258.           end do
  4259.       endif
  4260.  
  4261.       Packet = char(sSync)                !Clear packet/install sync byte
  4262.       P1cket(2) = toChar( len+2+sCheck )  !...and the length byte
  4263.       P1cket(3) = toChar( num )           !...and the sequence number
  4264.       P1cket(4) = type                    !...and the packet type
  4265.       Packet(5:) = pData                  !...add the data part
  4266.       p = 5 + len                         !Adjust the store-pointer
  4267.       P1cket(p) = toChar(check1(P1cket,p))!Perform/store the checksum
  4268.       p = p + 1                           !Bump pointer for EOL character
  4269.       if (sCheck .ne. 1) then             !Do 2- or 3-byte checks?
  4270.           P1cket(p) = toChar(check2())    !Yes - add the 2nd byte
  4271.           p = p + 1
  4272.       endif
  4273.       if (sCheck .eq. 3) then             !Doing 3-byte CRC?
  4274.           P1cket(p) = toChar(check3())    !Yes - add last byte
  4275.           p = p + 1
  4276.       endif
  4277.       P1cket(p) = char(sEOL)              !Voila - packet is ready to send
  4278.  
  4279.       call kdebug(packets,'SndPack: ',packet(:p))
  4280.  
  4281.       call putpak(p)                      !send the packet
  4282.  
  4283.       spak = spak + 1                     !Count the packet
  4284.  
  4285.       return
  4286.       end
  4287.  
  4288.       integer*2 function Check1(pack,plen)               ,<890525.1144>
  4289.      >Generate checksums
  4290.       implicit none
  4291.  
  4292. !     When called as Check1, this function calculates the 1- or 2-byte
  4293. !     checksum or a 3-byte CRC, AS DICTATED BY MY PARTNER'S PARAMETERS!
  4294. !     The part of the packet which is subject to checking is the length
  4295. !     field through the current value of "P" (the pointer to the next
  4296. !     byte of the packet) less 1.  It is called either to encode the
  4297. !     checksum/CRC or to test it on a received packet.  The returned
  4298. !     value of Check1 is the first of a possibly multi-byte checksum or
  4299. !     CRC.  Check2 and Check3 return the remaining bytes of the 2-byte
  4300. !     checksum or 3-byte CRC as needed.  Note that the form of the 1st
  4301. !     byte of the 1- and 2-byte checksums is NOT the same!.
  4302.  
  4303.       include kercom.ftni,NOLIST
  4304.       character*1 pack(*)
  4305.       integer*2   plen,i,csum,b
  4306.       integer*2   check2,check3
  4307.       integer*2   x,y,crc1(0:15),crc2(0:15)
  4308.       data crc1 /
  4309.      >000000b,010201b,020402b,030603b,041004b,051205b,061406b,071607b,
  4310.      >102010b,112211b,122412b,132613b,143014b,153215b,163416b,173617b/
  4311.       data crc2 /
  4312.      >000000b,010611b,021422b,031233b,043044b,053655b,062466b,072277b,
  4313.      >106110b,116701b,127532b,137323b,145154b,155745b,164576b,174367b/
  4314.  
  4315.       csum = 0                            !Clear checksum accumulator
  4316.  
  4317.       do i = 2,plen-1                     !Checksum length thru data
  4318.           b = ichar( pack(i) )            !Get a packet byte
  4319.           if (sCheck .ne. 3) then         !Doing 1- or 2-byte checks?
  4320.               csum = csum + b
  4321.           else                            !else doing CCITT-CRC
  4322.               b = ixor(b,csum)
  4323.               x = iand(b,17b)             !Get lower nybble
  4324.               y = ibits(b,4,4)            !...and upper nybble
  4325.               b = ixor(crc2(x),crc1(y))   !Get CRC factor
  4326.               csum = ibits(csum,8,8)      !Shift off byte from previous CRC
  4327.               csum = ixor(csum,b)         !and add in new value
  4328.           endif
  4329.       enddo
  4330.  
  4331.       if (sCheck .eq. 1) then             !Return proper 1-byte checksum
  4332.           check1 = iand( 63,(csum + (iand(csum,192)/64)))   !Form type-1 check
  4333.       elseif (sCheck .eq. 2) then         !Return 1st of 2-byte checksum
  4334.           check1 = ibits(csum,6,6)        !...as upper 6 of 12-bit checksum
  4335.       else                                !Return 1st of 3-byte CRC
  4336.           check1 = ibits(csum,12,4)       !...as upper 4 of 16-bit CRC
  4337.       endif
  4338.       return
  4339.  
  4340.       entry check2()
  4341.       if (sCheck .eq. 2) then             !Return 2nd of 2-byte checksum
  4342.           check2 = iand(csum,77b)         !...as lower 6 of 12-bit checksum
  4343.       else                                !else return 2nd of 3-byte CRC
  4344.           check2 = ibits(csum,6,6)        !...as bits 6-11 of 16-bit CRC
  4345.       endif
  4346.       return
  4347.  
  4348.       entry check3()
  4349.       check3 = iand(csum,77b)             !Return low 6 bits of CRC
  4350.       return
  4351.  
  4352.       end
  4353.  
  4354.       subroutine get                                     ,<890525.1144>
  4355.      >Get from a server
  4356.       implicit none
  4357.  
  4358.       include kercom.ftni,NOLIST
  4359.       include kercmd.ftni,NOLIST
  4360.       include kerfil.ftni,NOLIST
  4361.       logical*2 succeed,receive,fRetry,fRtryi
  4362.       integer*2 retry,err,len,num,TrimLen,j
  4363.       character*1 ptype,RecPack
  4364.  
  4365.       retry = 0                           !Reset the retry-counter
  4366.  
  4367.  5    if ( fRtryi(retry) ) then           !Retry-limit exceeded?
  4368.           call tpFm('Unable to initialize on a Get command')
  4369.           return
  4370.       endif
  4371.  
  4372.       call SndPar('I',seq)                !Try to initialize
  4373.       ptype = RecPack(len,num)            !Get the response
  4374.       if (ptype .eq. 'Y') then            !ACK?
  4375.           if (num .ne. seq) goto 5        !Retry if out of sequence
  4376.       endif
  4377.  
  4378.       seq = 0                             !Reset the sequence number
  4379.       retry = 0                           !...and retry counter
  4380.       pdata = GetMask                     !Set 1st param as get's data
  4381.       j = TrimLen(pdata)                  !Get the name's length
  4382.  
  4383.  10   if ( fRetry(retry) ) then           !Exceeded the retry limit?
  4384.           call tpFm('Unable to GET')
  4385.           return
  4386.       endif
  4387.  
  4388.       call SndPack('R',seq,j)             !Send the request for files
  4389.       ptype = RecPack(len,num)            !Get the response
  4390.  
  4391.       if (ptype .eq. 'S') then            !Correct response is send-init
  4392.           if (num .ne. seq) goto 10
  4393.           fSend = .false.                 !Do params as a receive
  4394.           call RecPar(len)                !get partner's parameters
  4395.           if (fBnry .and. Parity.ne.3 .and. .not.fBit8) then
  4396.               ErrMsg = 'Can''t receive binary file (parity problem)'
  4397.               call SndErr
  4398.               fBnry = .false.
  4399.               return
  4400.           endif
  4401.           call SndPar('Y',seq)            !...and send mine
  4402.           sCheck = NewChk                 !Change checksum type now
  4403.           seq = mod(seq+1,64)
  4404.           succeed = receive('F')          !Try to receive
  4405.           call success(succeed)
  4406.           sCheck = 1                      !Revert to 1-byte checksums
  4407.           return
  4408.       else if (ptype .eq. 'E') then       !Error packet?
  4409.           call tpCh('Received error packet: -',RecPkt(5:len+4),0)
  4410.           return
  4411.       else if (ptype .eq. 'X') then       !Checksum error?
  4412.           call tpFm('Checksum error - retrying')
  4413.       else if (ptype .eq. 'T') then       !Time-out?
  4414.           call tpFm('<timed out>')
  4415.       else
  4416.           call tpCh('Unknown packet type: -',ptype,1)
  4417.       endif
  4418.  
  4419.       goto 10
  4420.  
  4421.       end
  4422.  
  4423.       subroutine IBMrd                                   ,<890525.1144>
  4424.      >Look for IBM-prompt
  4425.       implicit none
  4426.  
  4427.       include kercom.ftni,NOLIST
  4428.       include kercnf.ftni,NOLIST          !To define remote configuration
  4429.       include kersta.ftni,NOLIST
  4430.       integer*2 xr(2),rc
  4431.       equivalence (xr(2),rc)
  4432.  
  4433.       if ( fIBM ) then                    !Need to wait for PROMPT?
  4434.           xr = R
  4435.           if ( btest(iRmx,0) ) then       !D-mux?
  4436.               Rc = 100b                   !Yes - just set for binary
  4437.           else
  4438.               Rc = 1100b                  !Binary + keep type-ahead data
  4439.           endif
  4440.           p = 18                          !Yes - read at least one byte
  4441.           do while (p .ne. 17)            !Look for prompt byte
  4442.               rbytes = rbytes + 1
  4443.               rovrhd = rovrhd + 1
  4444.               call xluex(1,xr,p,-1)
  4445.               p = ishft(p,-8)             !Move byte for look for IBM-PROMPT
  4446.           end do
  4447.       endif
  4448.       return
  4449.  
  4450.       end
  4451.  
  4452.       subroutine PakIO                                   ,<890525.1144>
  4453.      >Prepare for packet-I/O
  4454.       implicit none
  4455.  
  4456.       include kercom.ftni,NOLIST
  4457.       include kercnf.ftni,NOLIST
  4458.  
  4459.       if (R .eq. L) then                  !If remote-host mode...
  4460.           call disable(R,fRmx)            !...disable scheduling
  4461.           call KillEnqAck                 !...and handshake
  4462.       endif
  4463.       call control(R,2600b,1)             !Clear all input buffers
  4464.       fPkIO = .true.
  4465.       seq = 0                             !Reset sequence#
  4466.       return
  4467.  
  4468.       end
  4469.  
  4470.       subroutine NrmIO                                   ,<890525.1144>
  4471.      >Restore from packet-I/O
  4472.       implicit none
  4473.  
  4474.       include kercom.ftni,NOLIST
  4475.       include kercnf.ftni,NOLIST          !Defines fRmx
  4476.  
  4477.       sCheck = 1                          !Revert to 1-byte checksums
  4478.       fPkIO = .false.
  4479.       if (R .eq. L) then
  4480.           call restore(R)
  4481.           call enable(R,fRmx)
  4482.       endif
  4483.       return
  4484.  
  4485.       end
  4486.