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

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