home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / intel86b / kermit.p86 < prev    next >
Text File  |  2020-01-01  |  55KB  |  1,437 lines

  1. $large  optimize(3)
  2.  
  3. Kermit: do;
  4.  
  5. /*
  6.  *      K e r m i t   File Transfer Utility
  7.  *
  8.  *      iRMX-86 Kermit, Version 2.41
  9.  *      by Albert J. Goodman, Grinnell College
  10.  *
  11.  *      Main module, containg the main program and all commands.
  12.  *      Edit date:  22-August-1985
  13.  */
  14.  
  15.  
  16. declare
  17.                 /* CONSTANTS */
  18.  
  19.             /* Useful text substitutions */
  20.     boolean                 literally   'byte',     /* define a new type */
  21.     TRUE                    literally   '0FFh',     /* and constants */
  22.     FALSE                   literally   '000h',     /*  of that type */
  23.     forever                 literally   'while TRUE',   /* a WHILE condition */
  24.  
  25.             /* ASCII control character constants */
  26.     NUL                     literally   '00h',  /* null */
  27.     SOH                     literally   '01h',  /* start-of-header */
  28.     CTRL$C                  literally   '03h',  /* CTRL/C */
  29.     BEL                     literally   '07h',  /* bell (beep) */
  30.     BS                      literally   '08h',  /* backspace */
  31.     HT                      literally   '09h',  /* horizontal tab */
  32.     LF                      literally   '0Ah',  /* line-feed */
  33.     CR                      literally   '0Dh',  /* carriage-return */
  34.     CTRL$R$BRAK             literally   '1Dh',  /* CTRL/] */
  35.     DEL                     literally   '7Fh',  /* delete (rubout) */
  36.  
  37.             /* String constants */
  38.     sign$on(*)              byte data( 48,
  39.         'iRMX-86 Kermit, Version 2.41  (AJG, 22-Aug-85)',CR,LF ),
  40.     prompt(*)               byte data( 16, 'iRMX-86 Kermit> ' ),
  41.     dots$string(*)          byte data( 7, ' . . . ' ),
  42.     ok$string(*)            byte data( 2, 'Ok' ),
  43.     currently$string(*)     byte data( 14, ' is currently ' ),
  44.  
  45.             /* Defaults for various Kermit parameters */
  46.     def$esc$char            literally   'CTRL$R$BRAK',
  47.     def$max$retry           literally   '10',
  48.     def$packet$len          literally   '80',
  49.     def$time$limit          literally   '10',
  50.     def$num$pad             literally   '0',
  51.     def$pad$char            literally   'NUL',
  52.     def$eol                 literally   'CR',
  53.     def$quote               literally   '''#''',
  54.  
  55.             /* GET$CONSOLE$CHAR return codes (see KERMIT$SYS) */
  56.     TIMEOUT                 literally   '0FFFFh',   /* Time limit expired */
  57.     CTRL$C$CODE             literally   '08003h',   /* CTRL/C abort */
  58.  
  59.             /* Other constants */
  60.     MAX$PACKET$LEN          literally   '94',
  61.     CONNECT$ESC$TIME$LIMIT  literally   '5',
  62.  
  63.  
  64.                 /* GLOBAL VARIABLES */
  65.  
  66.             /* Kermit parameters */
  67.     beep        boolean,        /* Whether to beep when finished */
  68.     debug       boolean public, /* Whether we're debugging the program */
  69.     max$retry   byte public,    /* Maximum number of times to retry a packet */
  70.     packet$len  byte public,    /* The maximum length packet to send */
  71.     time$limit  byte public,    /* Seconds to time out if nothing received */
  72.     num$pad     byte public,    /* The number of padding characters to send */
  73.     pad$char    byte public,    /* The padding character to send */
  74.     eol         byte public,    /* The EOL (end-of-line) character to send */
  75.     quote       byte public,    /* The control-quote character to be used */
  76.     esc$char    byte,   /* The "escape" character for CONNECT */
  77.  
  78.             /* Other Kermit variables */
  79.     state       byte public,  /* Current state (see Kermit Protocol Manual) */
  80.     seq         byte public,    /* The current sequence number (0 to 63) */
  81.     tries       byte public,    /* Number of times current packet retried */
  82.  
  83.             /* Buffers */
  84.     info        structure(      /* Buffer for the contents of a packet */
  85.                     len                 byte,
  86.                     ch(MAX$PACKET$LEN)  byte),
  87.     info2       structure(      /* Another packet buffer */
  88.                     len                 byte,
  89.                     ch(MAX$PACKET$LEN)  byte),
  90.  
  91.             /* Possible command keywords */
  92.     q$mark(*)               byte data( 1, '?' ),
  93.     exit$string(*)          byte data( 4, 'EXIT' ),
  94.     help$string(*)          byte data( 4, 'HELP' ),
  95.     send$string(*)          byte data( 4, 'SEND' ),
  96.     receive$string(*)       byte data( 7, 'RECEIVE' ),
  97.     get$string(*)           byte data( 3, 'GET' ),
  98.     connect$string(*)       byte data( 7, 'CONNECT' ),
  99.     bye$string(*)           byte data( 3, 'BYE' ),
  100.     logout$string(*)        byte data( 6, 'LOGOUT' ),
  101.     finish$string(*)        byte data( 6, 'FINISH' ),
  102.     set$string(*)           byte data( 3, 'SET' ),
  103.     show$string(*)          byte data( 4, 'SHOW' ),
  104.     beep$string(*)          byte data( 4, 'BEEP' ),
  105.     debug$string(*)         byte data( 5, 'DEBUG' ),
  106.     on$string(*)            byte data( 2, 'ON' ),
  107.     off$string(*)           byte data( 3, 'OFF' ),
  108.     escape$string(*)        byte data( 6, 'ESCAPE' ),
  109.     retry$string(*)         byte data( 5, 'RETRY' ),
  110.     packet$len$string(*)    byte data( 13, 'PACKET-LENGTH' ),
  111.     timeout$string(*)       byte data( 7, 'TIMEOUT' ),
  112.     padding$string(*)       byte data( 7, 'PADDING' ),
  113.     padchar$string(*)       byte data( 7, 'PADCHAR' ),
  114.     end$of$line$string(*)   byte data( 11, 'END-OF-LINE' ),
  115.     quote$string(*)         byte data( 5, 'QUOTE' ),
  116.     version$string(*)       byte data( 7, 'VERSION' ),
  117.     all$string(*)           byte data( 3, 'ALL' ),
  118.  
  119.             /* Command and parameter lists */
  120.     command$list(*)         pointer data(
  121.                                         @exit$string,
  122.                                         @send$string,
  123.                                         @receive$string,
  124.                                         @get$string,
  125.                                         @connect$string,
  126.                                         @bye$string,
  127.                                         @logout$string,
  128.                                         @finish$string,
  129.                                         @set$string,
  130.                                         @show$string,
  131.                                         @help$string ),
  132.     set$param$list(*)       pointer data(
  133.                                         @beep$string,
  134.                                         @debug$string,
  135.                                         @escape$string,
  136.                                         @retry$string,
  137.                                         @packet$len$string,
  138.                                         @timeout$string,
  139.                                         @padding$string,
  140.                                         @padchar$string,
  141.                                         @end$of$line$string,
  142.                                         @quote$string ),
  143.     show$param$list(*)      pointer data(
  144.                                         @version$string,
  145.                                         @beep$string,
  146.                                         @debug$string,
  147.                                         @escape$string,
  148.                                         @retry$string,
  149.                                         @packet$len$string,
  150.                                         @timeout$string,
  151.                                         @padding$string,
  152.                                         @padchar$string,
  153.                                         @end$of$line$string,
  154.                                         @quote$string,
  155.                                         @all$string ),
  156.     on$off$list(*)          pointer data(
  157.                                         @on$string,
  158.                                         @off$string ),
  159.  
  160.             /* Comand parsing information (defined in KERMIT$UTIL) */
  161.     num$keywords    byte external;  /* Number of keywords found */
  162.  
  163.  
  164. /*      External procedures defined in KERMIT$SYS   */
  165.  
  166. get$console$char: procedure( time$limit ) word external;
  167.     declare
  168.         time$limit  word;
  169. end get$console$char;
  170.  
  171. xmit$console$char: procedure( ch ) external;
  172.     declare
  173.         ch  byte;
  174. end xmit$console$char;
  175.  
  176. get$remote$char: procedure( time$limit ) word external;
  177.     declare
  178.         time$limit  word;
  179. end get$remote$char;
  180.  
  181. xmit$remote$char: procedure( ch ) external;
  182.     declare
  183.         ch  byte;
  184. end xmit$remote$char;
  185.  
  186. flush$input$buffer: procedure external;
  187. end flush$input$buffer;
  188.  
  189. print: procedure( string$ptr ) external;
  190.     declare
  191.         string$ptr  pointer;
  192. end print;
  193.  
  194. new$line: procedure external;
  195. end new$line;
  196.  
  197. exit$program: procedure external;
  198. end exit$program;
  199.  
  200. setup: procedure external;
  201. end setup;
  202.  
  203. get$first$file$name: procedure( keyword$num, info$ptr ) external;
  204.     declare
  205.         keyword$num     byte,
  206.         info$ptr        pointer;
  207. end get$first$file$name;
  208.  
  209. get$next$file$name: procedure( info$ptr ) external;
  210.     declare
  211.         info$ptr    pointer;
  212. end get$next$file$name;
  213.  
  214. finish$send: procedure external;
  215. end finish$send;
  216.  
  217. prepare$file$name: procedure( info$ptr ) external;
  218.     declare
  219.         info$ptr    pointer;
  220. end prepare$file$name;
  221.  
  222. open$file: procedure( name$ptr ) boolean external;
  223.     declare
  224.         name$ptr    pointer;
  225. end open$file;
  226.  
  227. create$file: procedure( name$ptr ) boolean external;
  228.     declare
  229.         name$ptr    pointer;
  230. end create$file;
  231.  
  232. close$file: procedure external;
  233. end close$file;
  234.  
  235. get$command$line: procedure( prompt$ptr ) external;
  236.     declare
  237.         prompt$ptr  pointer;
  238. end get$command$line;
  239.  
  240. do$help: procedure( num$params ) external;
  241.     declare
  242.         num$params  byte;
  243. end do$help;
  244.  
  245.  
  246. /*      External procedures defined in KERMIT$UTIL      */
  247.  
  248. upcase: procedure( x ) byte external;
  249.     declare
  250.         x   byte;
  251. end upcase;
  252.  
  253. next$seq: procedure( seq$num ) byte external;
  254.     declare
  255.         seq$num     byte;
  256. end next$seq;
  257.  
  258. previous$seq: procedure( seq$num ) byte external;
  259.     declare
  260.         seq$num     byte;
  261. end previous$seq;
  262.  
  263. show$char: procedure( ch ) external;
  264.     declare
  265.         ch  byte;
  266. end show$char;
  267.  
  268. show$dec$num: procedure( num ) external;
  269.     declare
  270.         num     word;
  271. end show$dec$num;
  272.  
  273. show$flag: procedure( flag ) external;
  274.     declare
  275.         flag    boolean;
  276. end show$flag;
  277.  
  278. send$packet: procedure( type, num, info$ptr ) external;
  279.     declare
  280.         ( type, num )   byte,
  281.         info$ptr        pointer;
  282. end send$packet;
  283.  
  284. receive$packet: procedure( num$ptr, info$ptr ) byte external;
  285.     declare
  286.         ( num$ptr, info$ptr )   pointer;
  287. end receive$packet;
  288.  
  289. send$kermit$params: procedure( info$ptr ) external;
  290.     declare
  291.         info$ptr    pointer;
  292. end send$kermit$params;
  293.  
  294. get$kermit$params: procedure( info$ptr ) external;
  295.     declare
  296.         info$ptr    pointer;
  297. end get$kermit$params;
  298.  
  299. read$packet$from$file: procedure( info$ptr ) external;
  300.     declare
  301.         info$ptr    pointer;
  302. end read$packet$from$file;
  303.  
  304. write$packet$to$file: procedure( info$ptr ) external;
  305.     declare
  306.         info$ptr    pointer;
  307. end write$packet$to$file;
  308.  
  309. error$msg: procedure( msg$ptr ) external;
  310.     declare
  311.         msg$ptr     pointer;
  312. end error$msg;
  313.  
  314. unknown$packet$type: procedure( type, packet$ptr ) external;
  315.     declare
  316.         type        byte,
  317.         packet$ptr  pointer;
  318. end unknown$packet$type;
  319.  
  320. too$many$retries: procedure external;
  321. end too$many$retries;
  322.  
  323. wrong$number: procedure external;
  324. end wrong$number;
  325.  
  326. parse$command: procedure external;
  327. end parse$command;
  328.  
  329. parse$dec$num: procedure( keyword$num, num$ptr ) boolean external;
  330.     declare
  331.         keyword$num     byte,
  332.         num$ptr         pointer;
  333. end parse$dec$num;
  334.  
  335. show$command: procedure( kp1, kp2, kp3 ) external;
  336.     declare
  337.         ( kp1, kp2, kp3 )   pointer;
  338. end show$command;
  339.  
  340. too$few$params: procedure( kp1, kp2, kp3 ) external;
  341.     declare
  342.         ( kp1, kp2, kp3 )   pointer;
  343. end too$few$params;
  344.  
  345. too$many$params: procedure( kp1, kp2, kp3 ) external;
  346.     declare
  347.         ( kp1, kp2, kp3 )   pointer;
  348. end too$many$params;
  349.  
  350. extra$params: procedure( kp1, kp2, kp3 ) external;
  351.     declare
  352.         ( kp1, kp2, kp3 )   pointer;
  353. end extra$params;
  354.  
  355. invalid$param: procedure( k$num, kp1, kp2, kp3 ) external;
  356.     declare
  357.         k$num               byte,
  358.         ( kp1, kp2, kp3 )   pointer;
  359. end invalid$param;
  360.  
  361. keyword$match: procedure( keyword$num, keyword$ptr, min$len ) boolean external;
  362.     declare
  363.         ( keyword$num, min$len )    byte,
  364.         keyword$ptr                 pointer;
  365. end keyword$match;
  366.  
  367. list$choices: procedure( kp1, kp2, kp3, list$ptr, list$last ) external;
  368.     declare
  369.         ( kp1, kp2, kp3, list$ptr )     pointer,
  370.         list$last                       byte;
  371. end list$choices;
  372.  
  373. get$filespec: procedure( keyword$num, info$ptr ) external;
  374.     declare
  375.         keyword$num     byte,
  376.         info$ptr        pointer;
  377. end get$filespec;
  378.  
  379. send$generic$command: procedure( info$ptr, info2$ptr ) boolean external;
  380.     declare
  381.         ( info$ptr, info2$ptr )     pointer;
  382. end send$generic$command;
  383.  
  384.  
  385. /*
  386.  *
  387.  *      Command implementation procedures
  388.  *
  389.  */
  390.  
  391.  
  392. exit: procedure;
  393.  
  394.     /*
  395.      *  Implement the EXIT command.
  396.      */
  397.  
  398.     if ( num$keywords > 1 ) then    /* a parameter followed EXIT */
  399.         call too$many$params( @exit$string, 0, 0 );
  400.     else
  401.         call exit$program;
  402.  
  403. end exit;
  404.  
  405.  
  406. connect: procedure;
  407.  
  408.     /*
  409.      *  Implement the CONNECT command by performing as a virtual
  410.      *  terminal to the remote system.  Everything coming from the
  411.      *  remote computer is sent out to the console screen, and
  412.      *  everything typed on the console keyboard, except for the
  413.      *  "escape" character, is passed through to the remote system.
  414.      *      The escape character is <Ctrl-]> by default; it can be
  415.      *  set by the SET ESCAPE command.
  416.      *      If the escape character is followed by "C" (in upper or
  417.      *  lower case) the connection is closed and you are returned to
  418.      *  the local Kermit's command level.
  419.      *      If the escape character is followed by itself (i.e. it
  420.      *  is typed twice) it will be sent (once) to the remote system,
  421.      *  since this is the only way to send the escape character to
  422.      *  the remote system in CONNECT.
  423.      *      If the escape character is followed by anything else, or
  424.      *  if nothing is typed on the console within CONNECT$ESC$TIME$LIMIT
  425.      *  seconds after the escape character, a message will be displayed
  426.      *  explaining the options and the connection will be continued.
  427.      */
  428.  
  429.     declare
  430.         keep$connected  boolean,
  431.         ch              word;   /* Current character (or TIMEOUT) */
  432.  
  433.     if ( num$keywords > 1 ) then    /* a parameter followed CONNECT */
  434.         call too$many$params( @connect$string, 0, 0 );
  435.     else
  436.       do;
  437.         /* Keep the user informed of what we're doing */
  438.         call print( @( 37,'[ Connecting to remote system; type "' ) );
  439.         call show$char( esc$char );
  440.         call print( @( 31,'C" to return to local Kermit. ]' ) );
  441.         call new$line;
  442.         call new$line;  /* Leave a blank line */
  443.  
  444.         /* begin the virtual terminal loop */
  445.         keep$connected = TRUE;
  446.         do while ( keep$connected );
  447.             /* Get the next character (if any) from the remote system */
  448.             ch = get$remote$char( 0 );  /* don't wait */
  449.             if ( ch <> TIMEOUT ) then   /* we got a character */
  450.                 call xmit$console$char( ch ); /* so print it on the console */
  451.             /* Get the next character (if any) from the console */
  452.             ch = get$console$char( 0 ); /* don't wait */
  453.             if ( ch <> TIMEOUT ) then   /* we got a character */
  454.               do;   /* Handle the console character */
  455.                 if ( ch = esc$char ) then   /* we got the escape character */
  456.                   do;   /* Handle the escape sequence */
  457.                     /* Get the next character from the console */
  458.                     ch = get$console$char( CONNECT$ESC$TIME$LIMIT );
  459.                     if ( upcase( ch ) = 'C' ) then  /* If it was C */
  460.                         keep$connected = FALSE;     /* Close the connection */
  461.                     else if ( ch = esc$char ) then  /* They typed it twice */
  462.                         /* Send the escape character to the remote system */
  463.                         call xmit$remote$char( esc$char );
  464.                     else    /* Otherwise tell them what's going on */
  465.                       do;
  466.                         call new$line;
  467.                         call print( @( 19,'[ You are connected' ) );
  468.                         call print( @( 22,' to the remote system.' ) );
  469.                         call new$line;
  470.                         call print( @( 8,'  Type "' ) );
  471.                         call show$char( esc$char );
  472.                         call print( @( 25,'C" to return to the local' ) );
  473.                         call print( @( 24,' Kermit''s command level.' ) );
  474.                         call new$line;
  475.                         call print( @( 8,'  Type "' ) );
  476.                         call show$char( esc$char );
  477.                         call show$char( esc$char );
  478.                         call print( @( 12,'" to send a ' ) );
  479.                         call show$char( esc$char );
  480.                         call print( @( 22,' to the remote system.' ) );
  481.                         call new$line;
  482.                         call print( @( 8,'  Type "' ) );
  483.                         call show$char( esc$char );
  484.                         call print( @( 23,'?" to see this message.' ) );
  485.                         call new$line;
  486.                         call print( @( 26,'  Connection continuing. ]' ) );
  487.                         call new$line;
  488.                       end;  /* else */
  489.                   end;  /* if ( ch = esc$char ) */
  490.                 else if ( ch = CTRL$C$CODE ) then   /* we got a CTRL/C */
  491.                     call xmit$remote$char( CTRL$C );    /* so send one */
  492.                 else    /* we got an ordinary character from the console */
  493.                     call xmit$remote$char( ch ); /* Send it to remote system */
  494.               end;  /* if ( ch <> TIMEOUT ) */
  495.         end;    /* do while ( keep$connected ) */
  496.  
  497.         /* Keep the user informed */
  498.         call new$line;
  499.         call print( @( 21,'[ Connection closed, ' ) );
  500.         call print( @( 23,'back at local Kermit. ]' ) );
  501.       end;  /* else -- no parameter */
  502.  
  503. end connect;
  504.  
  505.  
  506. bye: procedure;
  507.  
  508.     /*
  509.      *  Implement the BYE command.
  510.      */
  511.  
  512.     if ( num$keywords > 1 ) then    /* a parameter followed BYE */
  513.         call too$many$params( @bye$string, 0, 0 );
  514.     else
  515.       do;   /* Perform the BYE command */
  516.         /* Send Generic Kermit Logout/bye command */
  517.         if send$generic$command( @( 1,'L' ), @info2 ) then
  518.             call exit$program;  /* ACK'd O.K., so exit the program--bye! */
  519.         call new$line;
  520.         call error$msg( @( 15,'Command failed.' ) );
  521.       end;  /* else */
  522.  
  523. end bye;
  524.  
  525.  
  526. finish: procedure;
  527.  
  528.     /*
  529.      *  Implement the FINISH command.
  530.      */
  531.  
  532.     if ( num$keywords > 1 ) then
  533.         call too$many$params( @finish$string, 0, 0 );
  534.     else
  535.       do;
  536.         /* Send Generic Kermit Finish command */
  537.         if send$generic$command( @( 1,'F' ), @info2 ) then
  538.             call print( @ok$string );  /* tell them it went O.K. */
  539.         else
  540.           do;
  541.             call new$line;
  542.             call error$msg( @( 15,'Command failed.' ) );
  543.           end;
  544.       end;  /* else */
  545.  
  546. end finish;
  547.  
  548.  
  549. logout: procedure;
  550.  
  551.     /*
  552.      *  Implement the LOGOUT command.
  553.      */
  554.  
  555.     if ( num$keywords > 1 ) then
  556.         call too$many$params( @logout$string, 0, 0 );
  557.     else
  558.       do;
  559.         /* Send the Generic Kermit Logout command */
  560.         if send$generic$command( @( 1,'L' ), @info2 ) then
  561.             call print( @ok$string );  /* tell them it went O.K. */
  562.         else
  563.           do;
  564.             call new$line;
  565.             call error$msg( @( 15,'Command failed.' ) );
  566.           end;
  567.       end;  /* else */
  568.  
  569. end logout;
  570.  
  571.  
  572. help: procedure;
  573.  
  574.     /*
  575.      *  Implement the HELP command.
  576.      */
  577.  
  578.     /* Invoke the HELP program */
  579.     call do$help( num$keywords - 1 );
  580.  
  581. end help;
  582.  
  583.  
  584. set: procedure;
  585.  
  586.     /*
  587.      *  Implement the SET command by dispatching to the appropriate
  588.      *  routine based on the second keyword (the parameter following SET).
  589.      */
  590.  
  591.     set$flag: procedure( kp2, flag$ptr );
  592.  
  593.         /*
  594.          *  SET a flag.  KP2 points to the flag's name and
  595.          *  FLAG$PTR points the the boolean variable to be set.
  596.          *  ON means set the flag TRUE, OFF means FALSE.
  597.          */
  598.  
  599.         declare
  600.             ( kp2, flag$ptr )   pointer,
  601.             flag based flag$ptr boolean;
  602.  
  603.         if ( num$keywords < 3 ) then
  604.             call too$few$params( @set$string, kp2, 0 );
  605.         else if ( num$keywords > 3 ) then
  606.             call extra$params( @set$string, kp2, 0 );
  607.         else if keyword$match( 2, @q$mark, 1 ) then
  608.             call list$choices( @set$string, kp2, 0,
  609.                                 @on$off$list, last( on$off$list ) );
  610.         else if keyword$match( 2, @on$string, 2 ) then
  611.           do;
  612.             flag = TRUE;
  613.             call print( @ok$string );
  614.           end;
  615.         else if keyword$match( 2, @off$string, 2 ) then
  616.           do;
  617.             flag = FALSE;
  618.             call print( @ok$string );
  619.           end;
  620.         else
  621.             call invalid$param( 2, @set$string, kp2, 0 );
  622.  
  623.     end set$flag;
  624.  
  625.  
  626.     set$byte: procedure( kp2, byte$ptr );
  627.  
  628.         /*
  629.          *  SET a byte variable.  KP2 points to its name, BYTE$PTR
  630.          *  points to the byte variable.  A decimal number is used.
  631.          */
  632.  
  633.         declare
  634.             ( kp2, byte$ptr )   pointer,
  635.             num based byte$ptr  byte,
  636.             new$num             word;
  637.  
  638.         if ( num$keywords < 3 ) then
  639.             call too$few$params( @set$string, kp2, 0 );
  640.         else if ( num$keywords > 3 ) then
  641.             call extra$params( @set$string, kp2, 0 );
  642.         else if keyword$match( 2, @q$mark, 1 ) then
  643.           do;
  644.             call show$command( @set$string, kp2, 0 );
  645.             call print( @( 38,' must be followed by a decimal number.' ) );
  646.           end;  /* if keyword$match( 2, @q$mark, 1 ) */
  647.         else
  648.           do;
  649.             if ( parse$dec$num( 2, @new$num ) ) then
  650.               do;
  651.                 num = new$num;
  652.                 call print( @ok$string );
  653.               end;  /* if -- valid number */
  654.             else
  655.                 call invalid$param( 2, @set$string, kp2, 0 );
  656.           end;  /* else */
  657.  
  658.     end set$byte;
  659.  
  660.  
  661.     /* begin SET */
  662.     if ( num$keywords < 2 ) then    /* there was no second keyword */
  663.         call too$few$params( @set$string, 0, 0 );
  664.     else if keyword$match( 1, @q$mark, 1 ) then
  665.         call list$choices( @set$string, 0, 0,
  666.                                 @set$param$list,
  667.                                 last( set$param$list ) );
  668.     else if keyword$match( 1, @escape$string, 2 ) then
  669.         call set$byte( @escape$string, @esc$char );
  670.     else if keyword$match( 1, @beep$string, 1 ) then
  671.         call set$flag( @beep$string, @beep );
  672.     else if keyword$match( 1, @debug$string, 1 ) then
  673.         call set$flag( @debug$string, @debug );
  674.     else if keyword$match( 1, @retry$string, 1 ) then
  675.         call set$byte( @retry$string, @max$retry );
  676.     else if keyword$match( 1, @packet$len$string, 3 ) then
  677.         call set$byte( @packet$len$string, @packet$len );
  678.     else if keyword$match( 1, @timeout$string, 1 ) then
  679.         call set$byte( @timeout$string, @time$limit );
  680.     else if keyword$match( 1, @padding$string, 4 ) then
  681.         call set$byte( @padding$string, @num$pad );
  682.     else if keyword$match( 1, @padchar$string, 4 ) then
  683.         call set$byte( @padchar$string, @pad$char );
  684.     else if keyword$match( 1, @end$of$line$string, 2 ) then
  685.         call set$byte( @end$of$line$string, @eol );
  686.     else if keyword$match( 1, @quote$string, 1 ) then
  687.         call set$byte( @quote$string, @quote );
  688.     else    /* unknown parameter */
  689.         call invalid$param( 1, @set$string, 0, 0 );
  690.  
  691. end set;
  692.  
  693.  
  694. show: procedure;
  695.  
  696.     /*
  697.      *  Implement the SHOW command by dispatching to the appropriate
  698.      *  routine based on the second keyword (the parameter after SHOW).
  699.      */
  700.  
  701.     show$version: procedure;
  702.  
  703.         /*  Implement the SHOW VERSION command */
  704.  
  705.         if ( num$keywords > 2 ) then
  706.             call too$many$params( @show$string, @version$string, 0 );
  707.         else
  708.           do;
  709.             call print( @( 8,'This is ' ) );
  710.             call print( @sign$on );
  711.           end;
  712.  
  713.     end show$version;
  714.  
  715.  
  716.     show$flag$value: procedure( kp2, flag$ptr );
  717.  
  718.         /*
  719.          *  Show the value of a flag.  KP2 points to its name,
  720.          *  and FLAG$PTR points to the boolean variable.
  721.          */
  722.  
  723.         declare
  724.             ( kp2, flag$ptr )   pointer,
  725.             flag based flag$ptr boolean;
  726.  
  727.         if ( num$keywords > 2 ) then
  728.             call too$many$params( @show$string, kp2, 0 );
  729.         else
  730.           do;
  731.             call print( kp2 );
  732.             call print( @currently$string );
  733.             call show$flag( flag );
  734.             call new$line;
  735.           end;  /* else */
  736.  
  737.     end show$flag$value;
  738.  
  739.  
  740.     show$byte: procedure( kp2, byte$ptr, char$flag, msg$ptr );
  741.  
  742.         /*
  743.          *  SHOW a byte variable.  KP2 points to its keyword name,
  744.          *  BYTE$PTR points to the byte itself, MSG$PTR points to
  745.          *  the message to be displayed before its value, and
  746.          *  CHAR$FLAG is TRUE if it is a character.
  747.          */
  748.  
  749.         declare
  750.             ( kp2, byte$ptr, msg$ptr )  pointer,
  751.             char$flag                   boolean,
  752.             num based byte$ptr          byte;
  753.  
  754.         if ( num$keywords > 2 ) then
  755.             call too$many$params( @show$string, kp2, 0 );
  756.         else
  757.           do;
  758.             call print( msg$ptr );
  759.             call print( @currently$string );
  760.             if ( char$flag ) then
  761.               do;
  762.                 call show$char( num );
  763.                 call print( @( 8,', ASCII ' ) );
  764.               end;  /* if ( char$flag ) */
  765.             call show$dec$num( num );
  766.             call print( @( 10,' (decimal)' ) );
  767.             call new$line;
  768.           end;  /* else */
  769.  
  770.     end show$byte;
  771.  
  772.  
  773.     show$all: procedure;
  774.  
  775.         /*  Implement the SHOW ALL command. */
  776.  
  777.         if ( num$keywords > 2 ) then
  778.             call too$many$params( @show$string, @all$string, 0 );
  779.         else
  780.           do;   /* show all the things we can show */
  781.             call show$version;
  782.             call show$flag$value( @beep$string, @beep );
  783.             call show$flag$value( @debug$string, @debug );
  784.             call show$byte( @escape$string, @esc$char, TRUE,
  785.                 @( 34,'The "escape" character for CONNECT' ) );
  786.             call show$byte( @retry$string, @max$retry, FALSE,
  787.                 @( 31,'Maximum times to retry a packet' ) );
  788.             call show$byte( @packet$len$string, @packet$len, FALSE,
  789.                 @( 29,'Maximum length packet to send' ) );
  790.             call show$byte( @timeout$string, @time$limit, FALSE,
  791.                 @( 37,'Seconds to wait for receive character' ) );
  792.             call show$byte( @padding$string, @num$pad, FALSE,
  793.                 @( 36,'Number of padding characters to send' ) );
  794.             call show$byte( @padchar$string, @pad$char, TRUE,
  795.                 @( 25,'Padding character to send' ) );
  796.             call show$byte( @end$of$line$string, @eol, TRUE,
  797.                 @( 29,'End-of-line character to send' ) );
  798.             call show$byte( @quote$string, @quote, TRUE,
  799.                 @( 25,'Control-quoting character' ) );
  800.           end;  /* else -- no extra parameter */
  801.  
  802.     end show$all;
  803.  
  804.  
  805.     /* begin SHOW */
  806.     if ( num$keywords < 2 ) then    /* there was no second keyword */
  807.         call too$few$params( @show$string, 0, 0 );
  808.     else if keyword$match( 1, @q$mark, 1 ) then
  809.         call list$choices( @show$string, 0, 0,
  810.                             @show$param$list,
  811.                             last( show$param$list ) );
  812.     else if keyword$match( 1, @version$string, 1 ) then
  813.         call show$version;
  814.     else if keyword$match( 1, @beep$string, 1 ) then
  815.         call show$flag$value( @beep$string, @beep );
  816.     else if keyword$match( 1, @debug$string, 1 ) then
  817.         call show$flag$value( @debug$string, @debug );
  818.     else if keyword$match( 1, @escape$string, 2 ) then
  819.         call show$byte( @escape$string, @esc$char, TRUE,
  820.             @( 34,'The "escape" character for CONNECT' ) );
  821.     else if keyword$match( 1, @retry$string, 1 ) then
  822.          call show$byte( @retry$string, @max$retry, FALSE,
  823.             @( 31,'Maximum times to retry a packet' ) );
  824.     else if keyword$match( 1, @packet$len$string, 3 ) then
  825.         call show$byte( @packet$len$string, @packet$len, FALSE,
  826.             @( 29,'Maximum length packet to send' ) );
  827.     else if keyword$match( 1, @timeout$string, 1 ) then
  828.         call show$byte( @timeout$string, @time$limit, FALSE,
  829.             @( 37,'Seconds to wait for receive character' ) );
  830.     else if keyword$match( 1, @padding$string, 4 ) then
  831.         call show$byte( @padding$string, @num$pad, FALSE,
  832.             @( 36,'Number of padding characters to send' ) );
  833.     else if keyword$match( 1, @padchar$string, 4 ) then
  834.         call show$byte( @padchar$string, @pad$char, TRUE,
  835.             @( 25,'Padding character to send' ) );
  836.     else if keyword$match( 1, @end$of$line$string, 2 ) then
  837.         call show$byte( @end$of$line$string, @eol, TRUE,
  838.             @( 29,'End-of-line character to send' ) );
  839.     else if keyword$match( 1, @quote$string, 1 ) then
  840.         call show$byte( @quote$string, @quote, TRUE,
  841.             @( 25,'Control-quoting character' ) );
  842.     else if keyword$match( 1, @all$string, 1 ) then
  843.         call show$all;
  844.     else
  845.         call invalid$param( 1, @show$string, 0, 0 );
  846.  
  847. end show;
  848.  
  849.  
  850. send: procedure;
  851.  
  852.     /*
  853.      *  Implement the SEND command.
  854.      */
  855.  
  856.     send$init: procedure;
  857.  
  858.         /*  Implement the Send-initiate state. */
  859.  
  860.         declare
  861.             ( type, num )   byte;   /* Incoming packet type, number */
  862.  
  863.         tries = ( tries + 1 );  /* count a try */
  864.         if ( tries > max$retry ) then       /* too many */
  865.             call too$many$retries;  /* abort */
  866.         else
  867.           do;   /* Send a Send-init packet */
  868.             call flush$input$buffer;
  869.             call send$kermit$params( @info2 );  /* Load our parameters */
  870.             call send$packet( 'S', seq, @info2 );   /* Send-initiate */
  871.             type = receive$packet( @num, @info2 );  /* Get the response */
  872.             /* If we got an acknowledgement with the proper number */
  873.             if ( ( type = 'Y' ) and ( num = seq ) ) then
  874.               do;
  875.                 call get$kermit$params( @info2 );   /* Extract their params */
  876.                 tries = 0;      /* reset try count */
  877.                 seq = next$seq( seq );  /* bump sequence number */
  878.                 if ( open$file( @info ) ) then  /* open the file to be sent */
  879.                   do;   /* it was successfully opened */
  880.                     /* Keep the user informed of our progress */
  881.                     call print( @( 13,'Sending file ' ) );
  882.                     call print( @info );
  883.                     call print( @dots$string );
  884.                     call prepare$file$name( @info );
  885.                     state = 'F';    /* go to send-file state */
  886.                   end;  /* if ( open$file( @info ) ) */
  887.                 else    /* couldn't open file */
  888.                     state = 'A';    /* abort--error message already given */
  889.               end;  /* if ( ( type = 'Y' ) and ( num = seq ) ) */
  890.             else if ( type = 0FFh ) then    /* CTRL/C abort */
  891.                 state = 0FFh;
  892.             else if ( ( type <> 'Y' ) and ( type <> 'N' )
  893.                         and ( type <> 0 ) ) then    /* got wrong type packet */
  894.                 call unknown$packet$type( type, @info2 );   /* abort */
  895.             /* Don't change state if got NAK, bad ACK, or nothing at all */
  896.           end;  /* else -- send send-init */
  897.  
  898.     end send$init;
  899.  
  900.  
  901.     send$file$data: procedure;
  902.  
  903.         /*  Implement the Send File-header and Send file-Data states */
  904.  
  905.         declare
  906.             ( type, num )   byte;   /* Incoming packet type, number */
  907.  
  908.         tries = ( tries + 1 );  /* count a try */
  909.         if ( tries > max$retry ) then       /* too many */
  910.             call too$many$retries;  /* abort */
  911.         else
  912.           do;   /* Send packet (file-name or data) */
  913.             call send$packet( state, seq, @info );
  914.             type = receive$packet( @num, @info2 );   /* get reply */
  915.             /* If got ACK for this packet or NAK for next one */
  916.             if ( ( ( type = 'N' ) and ( num = next$seq( seq ) ) ) or
  917.                     ( ( type = 'Y' ) and ( num = seq ) ) ) then
  918.               do;
  919.                 tries = 0;  /* reset try count */
  920.                 seq = next$seq( seq );  /* bump sequence number */
  921.                 call read$packet$from$file( @info );    /* Load data packet */
  922.                 if ( info.len = 0 ) then    /* end-of-file */
  923.                     state = 'Z';    /* so go to end-of-file state */
  924.                 else    /* data ready to be sent */
  925.                     state = 'D';    /* go to (or stay in) send-Data state */
  926.               end;  /* if ... */
  927.             else if ( type = 0FFh ) then    /* CTRL/C abort */
  928.                 state = 0FFh;
  929.             else if ( ( type <> 'Y' ) and ( type <> 'N' )
  930.                             and ( type <> 0 ) ) then
  931.                 call unknown$packet$type( type, @info2 );   /* abort */
  932.             /* If get NAK, bad ACK, or nothing at all, state doesn't change */
  933.           end;  /* else -- send packet */
  934.  
  935.     end send$file$data;
  936.  
  937.  
  938.     send$eof: procedure;
  939.  
  940.         /*  Implement the Send-end-of-file state */
  941.  
  942.         declare
  943.             ( type, num )   byte;   /* Incoming packet type, number */
  944.  
  945.         tries = ( tries + 1 );  /* count a try */
  946.         if ( tries > max$retry ) then       /* too many */
  947.             call too$many$retries;  /* abort */
  948.         else
  949.           do;   /* Send EOF packet */
  950.             call send$packet( 'Z', seq, 0 );
  951.             type = receive$packet( @num, @info2 );  /* Get reply */
  952.             /* If got ACK for this packet or NAK for next one */
  953.             if ( ( ( type = 'N' ) and ( num = next$seq( seq ) ) ) or
  954.                     ( ( type = 'Y' ) and ( num = seq ) ) ) then
  955.               do;
  956.                 call close$file;    /* close the file we're done sending */
  957.                 call print( @ok$string );  /* terminate the */
  958.                 call new$line;             /* "Sending file..." message */
  959.                 tries = 0;      /* reset try count */
  960.                 seq = next$seq( seq );  /* bump packet sequence number */
  961.                 call get$next$file$name( @info );   /* Get next file to send */
  962.                 if ( info.len = 0 ) then    /* no more files */
  963.                     state = 'B';    /* go to Break-transmission state */
  964.                 else    /* Another file to be sent */
  965.                   do;
  966.                     if ( open$file( @info ) ) then  /* open next file */
  967.                       do;   /* it was successfully opened */
  968.                         /* Keep the user informed of our progress */
  969.                         call print( @( 13,'Sending file ' ) );
  970.                         call print( @info );
  971.                         call print( @dots$string );
  972.                         call prepare$file$name( @info );
  973.                         state = 'F';    /* go to send-file state */
  974.                       end;  /* if ( open$file( @info ) ) */
  975.                     else    /* couldn't open file, so abort */
  976.                         state = 'A';    /* error message already given */
  977.                   end;  /* else -- another file to be sent */
  978.               end;  /* if ... */
  979.             else if ( type = 0FFh ) then    /* CTRL/C abort */
  980.                 state = 0FFh;
  981.             else if ( ( type <> 'Y' ) and ( type <> 'N' )
  982.                             and ( type <> 0 ) ) then
  983.                 call unknown$packet$type( type, @info2 );   /* abort */
  984.             /* If get NAK, bad ACK, or nothing at all, state doesn't change */
  985.           end;  /* else -- send EOF packet */
  986.  
  987.     end send$eof;
  988.  
  989.  
  990.     send$break: procedure;
  991.  
  992.         /*  Implement the Send-Break (End-of-Transmission) state */
  993.  
  994.         declare
  995.             ( type, num )   byte;   /* Incoming packet type, number */
  996.  
  997.         tries = ( tries + 1 );  /* count a try */
  998.         if ( tries > max$retry ) then       /* too many */
  999.             call too$many$retries;  /* abort */
  1000.         else
  1001.           do;   /* send the break (or EOT) packet */
  1002.             call send$packet( 'B', seq, 0 );
  1003.             type = receive$packet( @num, @info2 );  /* Get reply */
  1004.             /* If got ACK for this packet or NAK for next one */
  1005.             if ( ( ( type = 'N' ) and ( num = next$seq( seq ) ) ) or
  1006.                     ( ( type = 'Y' ) and ( num = seq ) ) ) then
  1007.               do;
  1008.                 tries = 0;      /* reset try count */
  1009.                 seq = next$seq( seq );  /* bump packet sequence number */
  1010.                 state = 'C';    /* and go to state Complete */
  1011.               end;  /* if ... */
  1012.             else if ( type = 0FFh ) then    /* CTRL/C abort */
  1013.                 state = 0FFh;
  1014.             else if ( ( type <> 'Y' ) and ( type <> 'N' )
  1015.                             and ( type <> 0 ) ) then
  1016.                 call unknown$packet$type( type, @info2 );   /* abort */
  1017.             /* If get NAK, bad ACK, or nothing at all, state doesn't change */
  1018.           end;  /* else -- send break packet */
  1019.  
  1020.     end send$break;
  1021.  
  1022.  
  1023.     /* begin SEND */
  1024.     if ( num$keywords < 2 ) then
  1025.       do;   /* tell them what kind of parameter is required */
  1026.         call print( @send$string );
  1027.         call print( @( 33,' must be followed by the filespec' ) );
  1028.         call print( @( 28,' for the file(s) to be sent.' ) );
  1029.       end;  /* if ( num$keywords < 2 ) */
  1030.     else if ( num$keywords > 2 ) then
  1031.         call extra$params( @send$string, 0, 0 );
  1032.     else    /* We have one parameter, the filespec */
  1033.       do;   /* perform the SEND command */
  1034.         /* Get first filename to send, using second keyword as filespec */
  1035.         call get$first$file$name( 1, @info );
  1036.         if ( info.len > 0 ) then    /* we got a valid filespec */
  1037.           do;   /* Implement the Send state-table switcher */
  1038.             state = 'S';    /* Start with Send-init state */
  1039.             seq = 0;        /* Initialize the packet sequence numbers */
  1040.             tries = 0;      /* no retries yet */
  1041.             /* do this as long as we're in a valid send state */
  1042.             do while ( ( state = 'S' ) or ( state = 'F' ) or ( state = 'D' )
  1043.                         or ( state = 'Z' ) or ( state = 'B' ) );
  1044.                 /* Dispatch to appropriate routine (they switch the state) */
  1045.                 if ( state = 'S' ) then
  1046.                     call send$init;
  1047.                 else if ( ( state = 'F' ) or ( state = 'D' ) ) then
  1048.                     call send$file$data;    /* two states share one routine */
  1049.                 else if ( state = 'Z' ) then
  1050.                     call send$eof;
  1051.                 else    /* state must be B */
  1052.                     call send$break;
  1053.             end;    /* do while ... */
  1054.             if ( beep ) then    /* Alert them that we finished */
  1055.                 call xmit$console$char( BEL );
  1056.             if ( state = 'C' ) then     /* proper completion */
  1057.                 call print( @( 14,'Send complete.' ) );
  1058.             else
  1059.               do;
  1060.                 call new$line;
  1061.                 if ( state = 0FFh ) then    /* it was because of CTRL/C */
  1062.                     call error$msg( @( 23,'Send aborted by CTRL/C.' ) );
  1063.                 else
  1064.                     call error$msg( @( 12, 'Send failed.' ) );
  1065.               end;
  1066.           end;  /* if ( info.len > 0 ) */
  1067.         else    /* invalid filespec */
  1068.             call print( @( 29,'Bad filespec, send cancelled.' ) );
  1069.         call finish$send;   /* Clean up after ITEMIZE */
  1070.       end;  /* else -- we have one parameter */
  1071.  
  1072. end send;
  1073.  
  1074.  
  1075. do$receive: procedure( get );
  1076.  
  1077.     /*
  1078.      *  Perform the RECEIVE (if GET is FALSE)
  1079.      *  or GET (if GET is TRUE) command.
  1080.      */
  1081.  
  1082.     declare
  1083.         get         boolean,
  1084.         oldtries    byte;   /* tries for previous packet */
  1085.  
  1086.     receive$init: procedure;
  1087.  
  1088.         /*  Implement the Receive Send-init state */
  1089.  
  1090.         declare
  1091.             type    byte;   /* Incoming packet type */
  1092.  
  1093.         tries = ( tries + 1 );  /* count a try */
  1094.         if ( tries > max$retry ) then   /* too many tries */
  1095.             call too$many$retries;  /* give up--go to Abort state */
  1096.         else
  1097.           do;   /* try to receive a Send-init packet */
  1098.             /* Get a packet, and set our sequence number to match its */
  1099.             type = receive$packet( @seq, @info2 );
  1100.             if ( type = 'S' ) then  /* we got one */
  1101.               do;
  1102.                 call get$kermit$params( @info2 );   /* extract their params */
  1103.                 call send$kermit$params( @info2 );  /* and load ours */
  1104.                 call send$packet( 'Y', seq, @info2 );   /* send ACK */
  1105.                 oldtries = tries;       /* save number of init tries */
  1106.                 tries = 0;      /* Reset try counter for next packet */
  1107.                 seq = next$seq( seq );  /* Go to next sequence number */
  1108.                 state = 'F';    /* And enter Receive-file state */
  1109.               end;  /* if ( type = 'S' ) */
  1110.             else if ( get and ( type = 'N' ) ) then
  1111.                 /* Got NAK to our Receive-init, so send it again */
  1112.                 call send$packet( 'R', seq, @info );
  1113.             else if ( type = 0FFh ) then    /* CTRL/C abort */
  1114.                 state = 0FFh;
  1115.             else if ( type = 0 ) then   /* got bad packet or none at all */
  1116.                 call send$packet( 'N', seq, 0 );    /* send NAK */
  1117.                 /* And will try again to receive--state didn't change */
  1118.             else    /* we got a packet, but wrong type */
  1119.                 call unknown$packet$type( type, @info2 );   /* abort */
  1120.           end;  /* else -- not too many tries yet */
  1121.  
  1122.     end receive$init;
  1123.  
  1124.  
  1125.     receive$file: procedure;
  1126.  
  1127.         /*  Implement the Receive-file state */
  1128.  
  1129.         declare
  1130.             ( type, num )   byte;   /* Incoming packet type, sequence num */
  1131.  
  1132.         tries = ( tries + 1 );  /* count a try */
  1133.         if ( tries > max$retry ) then   /* too many tries */
  1134.             call too$many$retries;  /* abort */
  1135.         else    /* get a packet */
  1136.           do;
  1137.             type = receive$packet( @num, @info );
  1138.             if ( type = 'S' ) then  /* it was a Send-init */
  1139.               do;
  1140.                 oldtries = ( oldtries + 1 );    /* Increment its tries */
  1141.                 if ( oldtries > max$retry ) then    /* too many */
  1142.                     call too$many$retries;  /* abort */
  1143.                 else if ( num = previous$seq( seq ) ) then
  1144.                   do;   /* It was the previous packet, so our ACK was lost */
  1145.                     call send$kermit$params( @info2 );  /* reload our params */
  1146.                     call send$packet( 'Y', num, @info2 );   /* previous ACK */
  1147.                     tries = 0;  /* reset tries for file-header packet */
  1148.                     /* state and seq don't change, already updated */
  1149.                   end;
  1150.                 else    /* wrong number */
  1151.                     call wrong$number;  /* abort */
  1152.               end;  /* if ( type = 'S' ) */
  1153.             else if ( type = 'Z' ) then     /* it was end-of-file */
  1154.               do;
  1155.                 oldtries = ( oldtries + 1 );    /* Increment its tries */
  1156.                 if ( oldtries > max$retry ) then    /* too many tries */
  1157.                     call too$many$retries;  /* abort */
  1158.                 else if ( num = previous$seq( seq ) ) then
  1159.                   do;   /* It was the previous packet, so our ACK was lost */
  1160.                     call send$packet( 'Y', num, 0 );    /* resend that ACK */
  1161.                     tries = 0;  /* reset tries for file-header */
  1162.                     /* state and seq don't change */
  1163.                   end;
  1164.                 else    /* wrong number */
  1165.                     call wrong$number;  /* abort */
  1166.               end;  /* if ( type = 'Z' ) */
  1167.             else if ( type = 'B' ) then  /* got Break */
  1168.               do;
  1169.                 if ( num = seq ) then   /* got right number */
  1170.                   do;
  1171.                     call send$packet( 'Y', seq, 0 );    /* ACK it */
  1172.                     state = 'C';    /* and go to complete state */
  1173.                   end;  /* if ( num = seq ) */
  1174.                 else    /* wrong number */
  1175.                     call wrong$number;  /* abort */
  1176.               end;  /* if ( type = 'B' ) */
  1177.             else if ( type = 'F' ) then     /* got File header */
  1178.               do;
  1179.                 if ( num = seq ) then   /* got right number */
  1180.                   do;
  1181.                     if ( create$file( @info ) ) then    /* create the file */
  1182.                       do;   /* file successfully created */
  1183.                         /* Keep the user informed of our progress */
  1184.                         call print( @( 15,'Receiving file ' ) );
  1185.                         call print( @info );    /* file name */
  1186.                         call print( @dots$string );
  1187.                         call send$packet( 'Y', seq, 0 );    /* ACK */
  1188.                         oldtries = tries;   /* save previous tries */
  1189.                         tries = 0;  /* and init new packet tries */
  1190.                         seq = next$seq( seq );  /* go to next packet number */
  1191.                         state = 'D';    /* and enter Receive-data state */
  1192.                       end;  /* if ( create$file( @info ) ) */
  1193.                     else    /* a problem creating the file, so abort */
  1194.                         state = 'A';    /* error message already given */
  1195.                   end;  /* if ( num = seq ) */
  1196.                 else    /* wrong number */
  1197.                     call wrong$number;  /* abort */
  1198.               end;  /* if ( type = 'F' ) */
  1199.             else if ( type = 0FFh ) then    /* got CTRL/C */
  1200.                 state = 0FFh;   /* signal CTRL/C abort */
  1201.             else if ( type = 0 ) then   /* got bad packet or none at all */
  1202.                 call send$packet( 'N', seq, 0 );    /* send NAK */
  1203.                 /* And will try again to receive--state didn't change */
  1204.             else    /* we got a packet, but wrong type */
  1205.                 call unknown$packet$type( type, @info );   /* abort */
  1206.           end;  /* else -- not too many tries */
  1207.  
  1208.     end receive$file;
  1209.  
  1210.  
  1211.     receive$data: procedure;
  1212.  
  1213.         /*  Implement the Receive-data state */
  1214.  
  1215.         declare
  1216.             ( type, num )   byte;   /* Incoming packet type, number */
  1217.  
  1218.         tries = ( tries + 1 );  /* count another try */
  1219.         if ( tries > max$retry ) then   /* too many */
  1220.             call too$many$retries;  /* abort */
  1221.         else
  1222.           do;
  1223.             type = receive$packet( @num, @info );   /* get a packet */
  1224.             if ( type = 'D' ) then  /* got Data packet */
  1225.               do;
  1226.                 if ( num = seq ) then   /* right number */
  1227.                   do;
  1228.                     call write$packet$to$file( @info );
  1229.                     call send$packet( 'Y', seq, 0 );    /* ACK it */
  1230.                     oldtries = tries;   /* save old try count */
  1231.                     tries = 0;          /* and start a new one */
  1232.                     seq = next$seq( seq );  /* go to next packet number */
  1233.                     /* Remain in Receive-Data state */
  1234.                   end;  /* if ( num = seq ) */
  1235.                 else    /* wrong number */
  1236.                   do;
  1237.                     oldtries = ( oldtries + 1 );
  1238.                     if ( oldtries > max$retry ) then
  1239.                         call too$many$retries;  /* too many tries, abort */
  1240.                     else if ( num = previous$seq( seq ) ) then
  1241.                       do;   /* got previous packet again */
  1242.                         call send$packet( 'Y', num, 0 );    /* ACK again */
  1243.                         tries = 0;      /* reset tries for this one */
  1244.                         /* Stay in D state */
  1245.                       end;  /* if ( num = previous$seq( seq ) ) */
  1246.                     else    /* totally wrong number */
  1247.                         call wrong$number;  /* abort */
  1248.                   end;  /* else -- wrong number */
  1249.               end;  /* if ( type = 'D' ) */
  1250.             else if ( type = 'F' ) then     /* got file-header */
  1251.               do;
  1252.                 oldtries = ( oldtries + 1 );
  1253.                 if ( oldtries > max$retry ) then
  1254.                     call too$many$retries;  /* abort */
  1255.                 else if ( num = previous$seq( seq ) ) then
  1256.                   do;   /* Got previous packet again */
  1257.                     call send$packet( 'Y', num, 0 );    /* ACK again */
  1258.                     tries = 0;      /* reset tries for this one */
  1259.                     /* State doesn't change */
  1260.                   end;  /* if ( num = previous$seq( seq ) ) */
  1261.                 else    /* wrong number */
  1262.                     call wrong$number;  /* abort */
  1263.               end;  /* if ( type = 'F' ) */
  1264.             else if ( type = 'Z' ) then     /* got end-of-file */
  1265.               do;
  1266.                 if ( num = seq ) then   /* right number */
  1267.                   do;
  1268.                     call close$file;    /* close the output file */
  1269.                     call print( @ok$string );  /* terminate the */
  1270.                     call new$line;     /* "Receiving file..." message */
  1271.                     call send$packet( 'Y', seq, 0 );    /* ACK */
  1272.                     oldtries = tries;   /* save old try count */
  1273.                     tries = 0;      /* and init a new one */
  1274.                     seq = next$seq( seq );  /* go to next packet number */
  1275.                     state = 'F';    /* and go to Receive-File state */
  1276.                   end;  /* if ( num = seq ) */
  1277.                 else    /* wrong number */
  1278.                     call wrong$number;  /* abort */
  1279.               end;  /* if ( type = 'Z' ) */
  1280.             else if ( type = 0FFh ) then
  1281.                 state = 0FFh;   /* signal CTRL/C abort */
  1282.             else if ( type = 0 ) then   /* got bad packet or none at all */
  1283.                 call send$packet( 'N', seq, 0 );    /* send NAK */
  1284.                 /* And will try again to receive--state didn't change */
  1285.             else    /* we got a packet, but wrong type */
  1286.                 call unknown$packet$type( type, @info );   /* abort */
  1287.           end;  /* else -- not too many tries */
  1288.  
  1289.     end receive$data;
  1290.  
  1291.  
  1292.     /* begin DO$RECEIVE */
  1293.     state = 'R';    /* Start with receive-init state */
  1294.     seq = 0;        /* initialize packet sequence number */
  1295.     tries = 0;      /* no retries yet */
  1296.     if ( get ) then
  1297.       do;   /* Request the file(s) from the server */
  1298.         call get$filespec( 1, @info );  /* get second keyword into INFO */
  1299.         call send$packet( 'R', seq, @info );    /* send Receive-initiate */
  1300.         /* And fall through to normal RECEIVE */
  1301.       end;  /* if ( get ) */
  1302.     /* Implement the Receive state-table switcher */
  1303.     /* do this as long as we're in a valid receive state */
  1304.     do while ( ( state = 'R' ) or ( state = 'F' ) or ( state = 'D' ) );
  1305.         /* Dispatch to appropriate routine (they switch the state) */
  1306.         if ( state = 'R' ) then
  1307.             call receive$init;
  1308.         else if ( state = 'F' ) then
  1309.             call receive$file;
  1310.         else    /* state must be D */
  1311.             call receive$data;
  1312.     end;    /* do while ... */
  1313.     if ( beep ) then    /* Alert them that we finished */
  1314.         call xmit$console$char( BEL );
  1315.     if ( state = 'C' ) then     /* proper completion */
  1316.         call print( @( 17,'Receive complete.' ) );
  1317.     else
  1318.       do;
  1319.         call new$line;
  1320.         if ( state = 0FFh ) then    /* it was because of CTRL/C */
  1321.             call error$msg( @( 26,'Receive aborted by CTRL/C.' ) );
  1322.         else
  1323.             call error$msg( @( 15,'Receive failed.' ) );
  1324.       end;
  1325.  
  1326. end do$receive;
  1327.  
  1328.  
  1329. receive: procedure;
  1330.  
  1331.     /*
  1332.      *  Implement the RECEIVE command.
  1333.      */
  1334.  
  1335.     if ( num$keywords > 1 ) then    /* a parameter followed RECEIVE */
  1336.         call too$many$params( @receive$string, 0, 0 );
  1337.     else    /* Perform the RECEIVE command */
  1338.         call do$receive( FALSE );
  1339.  
  1340. end receive;
  1341.  
  1342.  
  1343. get: procedure;
  1344.  
  1345.     /*
  1346.      *  Implement the GET command.
  1347.      */
  1348.  
  1349.     if ( num$keywords < 2 ) then
  1350.       do;   /* tell them what kind of parameter is required */
  1351.         call print( @get$string );
  1352.         call print( @( 33,' must be followed by the filespec' ) );
  1353.         call print( @( 30,' for the file(s) to be gotten.' ) );
  1354.       end;  /* if ( num$keywords < 2 ) */
  1355.     else if ( num$keywords > 2 ) then
  1356.         call extra$params( @get$string, 0, 0 );
  1357.     else    /* We have one parameter, the filespec */
  1358.         call do$receive( TRUE );    /* perform the GET command */
  1359.  
  1360. end get;
  1361.  
  1362.  
  1363. execute$command: procedure;
  1364.  
  1365.     /*
  1366.      *  Execute the command specified by the first keyword parsed
  1367.      *  from the command line.  If it is not a valid command, issue
  1368.      *  an appropriate error message to the console.
  1369.      */
  1370.  
  1371.     if keyword$match( 0, @q$mark, 1 ) then
  1372.         call list$choices( 0, 0, 0, @command$list, last( command$list ) );
  1373.     else if keyword$match( 0, @exit$string, 1 ) then
  1374.         call exit;
  1375.     else if keyword$match( 0, @help$string, 1 ) then
  1376.         call help;
  1377.     else if keyword$match( 0, @send$string, 3 ) then
  1378.         call send;
  1379.     else if keyword$match( 0, @receive$string, 1 ) then
  1380.         call receive;
  1381.     else if keyword$match( 0, @get$string, 1 ) then
  1382.         call get;
  1383.     else if keyword$match( 0, @connect$string, 1 ) then
  1384.         call connect;
  1385.     else if keyword$match( 0, @bye$string, 1 ) then
  1386.         call bye;
  1387.     else if keyword$match( 0, @logout$string, 1 ) then
  1388.         call logout;
  1389.     else if keyword$match( 0, @finish$string, 1 ) then
  1390.         call finish;
  1391.     else if keyword$match( 0, @set$string, 3 ) then
  1392.         call set;
  1393.     else if keyword$match( 0, @show$string, 2 ) then
  1394.         call show;
  1395.     else
  1396.         call invalid$param( 0, 0, 0, 0 );
  1397.     call new$line;  /* Make sure the next prompt starts on a new line */
  1398.  
  1399. end execute$command;
  1400.  
  1401.  
  1402. /*
  1403.  *
  1404.  *      Main program -- Kermit
  1405.  *
  1406.  */
  1407.  
  1408.  
  1409. /* begin KERMIT */
  1410. call new$line;
  1411. call print( @sign$on );     /* Identify who and what we are */
  1412. call new$line;
  1413.  
  1414. call setup;     /* Do system-dependent setup */
  1415.  
  1416. /* Initialize our parameters to their defaults */
  1417. beep = TRUE;    /* Beep unless told to shut up */
  1418. debug = FALSE;  /* We hope it doesn't need any more debugging... */
  1419. esc$char = def$esc$char;
  1420. max$retry = def$max$retry;
  1421. packet$len = def$packet$len;
  1422. time$limit = def$time$limit;
  1423. num$pad = def$num$pad;
  1424. pad$char = def$pad$char;
  1425. eol = def$eol;
  1426. quote = def$quote;
  1427.  
  1428. /* Begin the main command line loop */
  1429. do forever;     /* Do this until some command exits the program */
  1430.     call get$command$line( @prompt );   /* Get a command line */
  1431.     call parse$command;     /* Parse the command line */
  1432.     if ( num$keywords > 0 ) then    /* If we got at least one keyword */
  1433.         call execute$command;   /* perform the command requested */
  1434. end;    /* do forever */
  1435.  
  1436. end Kermit;
  1437.