home *** CD-ROM | disk | FTP | other *** search
/ kermit.columbia.edu / kermit.columbia.edu.tar / kermit.columbia.edu / old / ckermit5a190 / ckcpro.w < prev    next >
Text File  |  1994-09-17  |  36KB  |  1,127 lines

  1. char *protv = "C-Kermit Protocol Module 5A(072), 18 Sep 94"; /* -*-C-*- */
  2.  
  3. /* C K C P R O  -- C-Kermit Protocol Module, in Wart preprocessor notation. */
  4. /*
  5.   Author: Frank da Cruz (fdc@columbia.edu, FDCCU@CUVMA.BITNET),
  6.   Columbia University Academic Information Systems, New York City.
  7.  
  8.   Copyright (C) 1985, 1994, Trustees of Columbia University in the City of New
  9.   York.  The C-Kermit software may not be, in whole or in part, licensed or
  10.   sold for profit as a software product itself, nor may it be included in or
  11.   distributed with commercial products or otherwise distributed by commercial
  12.   concerns to their clients or customers without written permission of the
  13.   Office of Kermit Development and Distribution, Columbia University.  This
  14.   copyright notice must not be removed, altered, or obscured.
  15. */
  16. #include "ckcsym.h"
  17. #include "ckcdeb.h"
  18. #include "ckcasc.h"
  19. #include "ckcker.h"
  20. /*
  21.  Note -- This file may also be preprocessed by the UNIX Lex program, but 
  22.  you must indent the above #include statements before using Lex, and then
  23.  restore them to the left margin in the resulting C program before compilation.
  24.  Also, the invocation of the "wart()" function below must be replaced by an
  25.  invocation  of the "yylex()" function.  It might also be necessary to remove
  26.  comments in the %%...%% section.
  27. */
  28.  
  29. /* State definitions for Wart (or Lex) */
  30. %states ipkt rfile rattr rdata ssinit ssfile ssattr ssdata sseof sseot
  31. %states serve generic get rgen
  32.  
  33. /* External C-Kermit variable declarations */
  34.   extern char *versio, *srvtxt, *cmarg, *cmarg2, **cmlist, *rf_err;
  35.   extern char filnam[], ttname[];
  36.   extern CHAR sstate, *rpar(), *srvptr, *data;
  37.   extern int timint, rtimo, nfils, hcflg, xflg, flow, mdmtyp, network;
  38.   extern int rejection;
  39. #ifdef NETCONN
  40. #ifdef CK_SPEED
  41.   extern int ttnproto;            /* Network protocol */
  42.   extern short ctlp[];            /* Control-character prefix table */
  43. #endif /* CK_SPEED */
  44. #endif /* NETCONN */
  45.   extern int cxseen, czseen, server, srvdis, local, displa, bctu, bctr, bctl;
  46.   extern int quiet, tsecs, parity, backgrd, nakstate, atcapu, wslotn, winlo;
  47.   extern int wslots, success, xitsta, rprintf, discard, cdtimo, keep, fdispla;
  48.   extern int timef, stdinf, rscapu, sendmode, epktflg;
  49.   extern int binary, bsave, bsavef, savmod, fncnv;
  50.   extern long speed, ffc;
  51.   extern char *DIRCMD, *DIRCM2, *DELCMD, *TYPCMD, *SPACMD, *SPACM2, *WHOCMD;
  52.   extern CHAR *rdatap;
  53.   extern struct zattr iattr;
  54.  
  55. #ifdef pdp11
  56.   extern CHAR srvcmd[];
  57.   extern CHAR *pktmsg;
  58. #else
  59. #ifdef DYNAMIC
  60.   extern CHAR *srvcmd;
  61.   extern CHAR *pktmsg;
  62. #else
  63.   extern CHAR srvcmd[];
  64.   extern CHAR pktmsg[];
  65. #endif /* DYNAMIC */
  66. #endif /* pdp11 */
  67.  
  68. #ifndef NOSPL
  69.   extern int cmdlvl;
  70.   char querybuf[QBUFL+1] = { NUL, NUL }; /* QUERY response buffer */
  71.   char *qbufp = querybuf;        /* Pointer to it */
  72.   int qbufn = 0;            /* Length of data in it */
  73.   extern int query;            /* Query-active flag */
  74. #else
  75.   extern int tlevel;
  76. #endif /* NOSPL */
  77.  
  78. #ifdef NOMSEND
  79.   extern int sndsrc;
  80. #endif /* NOMSEND */
  81.  
  82. /* Flags for the ENABLE and DISABLE commands */
  83. extern int
  84.   en_cwd, en_del, en_dir, en_fin, en_get, en_bye,
  85.   en_hos, en_sen, en_spa, en_set, en_typ, en_who;
  86. #ifndef NOSPL
  87. extern int en_asg, en_que;
  88. #endif /* NOSPL */
  89.  
  90. /* Global variables declared here */
  91.  
  92.   int what = W_NOTHING;            /* What I am doing */
  93.   int whatru = 0;            /* What are you */
  94.  
  95. /* Local variables */
  96.  
  97.   static char vstate = 0;          /* Saved State   */
  98.   static char vcmd = 0;            /* Saved Command */
  99.  
  100.   static int x;                /* General-purpose integer */
  101.   static char *s;            /* General-purpose string pointer */
  102.  
  103. /* Macros - Note, BEGIN is predefined by Wart (and Lex) as "state = ", */
  104. /* BEGIN is NOT a GOTO! */
  105. #define TINIT  if (tinit() < 0) return(-9)
  106. #define SERVE  TINIT; nakstate = 1; what = W_NOTHING; cmarg2 = ""; BEGIN serve
  107. #ifdef COMMENT
  108. #define RESUME if (server) {SERVE;} else { if(!local)msleep(100); return(0); }
  109. #else
  110. #define RESUME if (server) { SERVE; } else { return(0); }
  111. #endif /* COMMENT */
  112. #define QUIT x=quiet; quiet=1; clsif(); clsof(1); tsecs=gtimer(); quiet=x; \
  113.  return(1)
  114.  
  115. %%
  116. /*
  117.   Protocol entry points, one for each start state (sstate).
  118.   The lowercase letters are internal "inputs" from the user interface.
  119. */
  120.  
  121. s { TINIT;                /* Do Send command */
  122.     if (sinit() >= 0) BEGIN ssinit;
  123.        else RESUME; }
  124.  
  125. v { TINIT; nakstate = 1; BEGIN get; }                    /* Receive */
  126.  
  127. r {                    /* Get */
  128.     TINIT;
  129.     vstate = get;
  130.     vcmd = 0;
  131.     if (sipkt('I') >= 0)
  132.       BEGIN ipkt;
  133.     else
  134.       RESUME;
  135. }
  136. c {                    /* Host */
  137.     TINIT;
  138.     vstate = rgen;
  139.     vcmd = 'C';
  140.     if (sipkt('I') >= 0)
  141.       BEGIN ipkt;
  142.     else
  143.       RESUME;
  144. }
  145. k { TINIT;                /* Kermit */
  146.     vstate = rgen;
  147.     vcmd = 'K';
  148.     if (sipkt('I') >= 0)
  149.       BEGIN ipkt;
  150.     else
  151.       RESUME;
  152. }
  153. g {                    /* Generic */
  154.     TINIT;
  155.     vstate = rgen;
  156.     vcmd = 'G';
  157.     if (sipkt('I') >= 0)
  158.       BEGIN ipkt;
  159.     else
  160.       RESUME;
  161. }
  162.  
  163. x { SERVE; }                /* Be a Server */
  164.  
  165. a {
  166.     int b1, b2;
  167.     if (!data) TINIT;            /* "ABEND" -- Tell other side. */
  168. #ifndef pdp11
  169.     if (epktflg) {            /* If because of E-PACKET command */
  170.     b1 = bctl; b2 = bctu;        /* Save block check type */
  171.     bctl = bctu = 1;        /* set it to 1 */
  172.     }
  173. #endif /* pdp11 */
  174.     errpkt((CHAR *)"User cancelled");    /* Send the packet */
  175. #ifndef pdp11
  176.     if (epktflg) {            /* Restore the block check */
  177.     epktflg = 0;
  178.     bctl = b1; bctu = b2;
  179.     }
  180.     screen(SCR_EM,0,0L,"User cancelled");
  181. #endif /* pdp11 */
  182.     success = 0;
  183.     return(0);                /* Return from protocol. */
  184. }
  185.  
  186. /*
  187.   Dynamic states: <current-states>input-character { action }
  188.   nakstate != 0 means we're in a receiving state, in which we send ACKs & NAKs.
  189. */
  190. <rgen,get,serve>S {            /* Receive Send-Init packet. */
  191.     if (state == serve && !en_sen) {    /* Not allowed if in server mode */
  192.     errpkt((CHAR *)"SEND disabled"); /* and SEND is disabled. */
  193.     SERVE;
  194.     } else {                /* OK to go ahead. */
  195.     nakstate = 1;            /* Can send NAKs from here. */
  196.     rinit(rdatap);            /* Set parameters */
  197.     bctu = bctr;            /* Switch to agreed-upon block check */
  198.     bctl = (bctu == 4) ? 2 : bctu;    /* Set block-check length */
  199.     what = W_RECV;            /* Remember we're receiving */
  200.     resetc();            /* Reset counters */
  201.     rtimer();            /* Reset timer */
  202.     BEGIN rfile;            /* Go into receive-file state */
  203.     }
  204. }
  205.  
  206. /* States in which we get replies back from commands sent to a server. */
  207. /* Complicated because direction of protocol changes, packet number    */
  208. /* stays at zero through I-G-S sequence, and complicated even more by  */
  209. /* sliding windows buffer allocation. */
  210.  
  211. <ipkt>Y {                /* Get ack for I-packet */
  212.     int x = 0;
  213.     spar(rdatap);            /* Set parameters */
  214.     winlo = 0;                /* Set window-low back to zero */
  215.     if (vcmd) {                /* If sending a generic command */
  216.     x = scmd(vcmd,(CHAR *)cmarg);    /* Do that */
  217.     vcmd = 0;            /* and then un-remember it. */
  218.     } else if (vstate == get)
  219.       x = srinit();            /* If sending GET command, do that. */
  220.     if (x < 0) {            /* If command was too long */
  221.     errpkt((CHAR *)"Command too long for server"); /* cancel both sides. */
  222.     ermsg("Command too long for server");
  223.     success = 0;
  224.     RESUME;
  225.     } else {
  226.     rtimer();            /* Reset the elapsed seconds timer. */
  227.     winlo = 0;            /* Window back to 0, again. */
  228.     nakstate = 1;            /* Can send NAKs from here. */
  229.     BEGIN vstate;            /* Switch to desired state */
  230.     }
  231. }
  232.  
  233. <ipkt>E {                /* Ignore Error reply to I packet */
  234.     int x = 0;
  235.     winlo = 0;                /* Set window-low back to zero */
  236.     if (vcmd) {                /* In case other Kermit doesn't */
  237.     x = scmd(vcmd,(CHAR *)cmarg);    /* understand I-packets. */
  238.     vcmd = 0;            /* Otherwise act as above... */
  239.     } else if (vstate == get) x = srinit();
  240.     if (x < 0) {            /* If command was too long */
  241.     errpkt((CHAR *)"Command too long for server"); /* cancel both sides. */
  242.     ermsg("Command too long for server");
  243.     success = 0;
  244.     RESUME;
  245.     } else {
  246.     winlo = 0;            /* Back to packet 0 again. */
  247.     freerpkt(winlo);        /* Discard the Error packet. */
  248.     nakstate = 1;            /* Can send NAKs from here. */
  249.     BEGIN vstate;
  250.     }
  251. }
  252.  
  253. <get>Y {        /* Resend of previous I-pkt ACK, same seq number! */
  254.     srinit();                /* Send the GET packet again. */
  255. }
  256.  
  257. /* States in which we're being a server */
  258.  
  259. <serve,get>I {                /* Get I-packet */
  260.     spar(rdatap);            /* Set parameters from it */
  261.     ack1(rpar());            /* Respond with our own parameters */
  262.     pktinit();                /* Reinitialize packet numbers */
  263. }
  264.  
  265. <serve>R {                /* Get Receive-Init (GET) */
  266.     debug(F100,"<serve>R","",0);
  267.     if (!en_get) {            /* Only if not disabled!  */
  268.     errpkt((CHAR *)"GET disabled");
  269.     SERVE;
  270.     } else {                /* OK to go ahead. */
  271. #ifdef WHATAMI
  272.     if (whatru & WM_FLAG) {        /* Did we get WHATAMI info? */
  273. #ifdef VMS
  274.         if (binary != XYFT_I && binary != XYFT_L)
  275. #else
  276. #ifdef OS2
  277.         if (binary != XYFT_L)
  278. #endif /* OS2 */
  279. #endif /* VMS */
  280.         binary = (whatru & WM_FMODE) ?  /* Yes, set transfer mode */
  281.           XYFT_B : XYFT_T;            /* automatically */
  282.         bsave = bsavef = savmod = 0;
  283.         fncnv = (whatru & WM_FNAME) ? 1 : 0; /* And name conversion */
  284.     }
  285. #endif /* WHATAMI */
  286.     srvptr = srvcmd;        /* Point to server command buffer */
  287.     decode(rdatap,putsrv,0);    /* Decode the GET command into it */
  288.     /* Accept multiple filespecs */
  289.         cmarg2 = "";            /* Don't use cmarg2 */
  290.     cmarg = "";            /* Don't use cmarg */
  291. #ifndef NOMSEND                /* New way. */
  292.     nfils = fnparse((char *)srvcmd); /* Use cmlist instead */
  293. #else
  294.     nfils = 0 - zxpand((char *)srvcmd);
  295. #endif /* NOMSEND */
  296.     nakstate = 0;            /* Now I'm the sender! */
  297.     if (sinit() >= 0) {        /* Send Send-Init */
  298.         timint = chktimo(rtimo,timef); /* Switch to per-packet timer */
  299.         BEGIN ssinit;        /* If successful, switch state */
  300.     } else { SERVE; }        /* Else back to server command wait */
  301.     }
  302. }
  303.  
  304. <serve>G {                /* Generic server command */
  305.     srvptr = srvcmd;            /* Point to command buffer */
  306.     decode(rdatap,putsrv,0);        /* Decode packet data into it */
  307.     putsrv('\0');            /* Insert a couple nulls */
  308.     putsrv('\0');            /* for termination */
  309.     if (srvcmd[0]) {
  310.     sstate = srvcmd[0];        /* Set requested start state */
  311.     nakstate = 0;            /* Now I'm the sender. */
  312.     what = W_REMO;            /* Doing a REMOTE command. */
  313.     if (timint < 1)
  314.       timint = chktimo(rtimo,timef); /* Switch to per-packet timer */
  315.     BEGIN generic;            /* Switch to generic command state */
  316.     } else {
  317.     errpkt((CHAR *)"Badly formed server command"); /* report error */
  318.     SERVE;                /* & go back to server command wait */
  319.     }
  320. }
  321.  
  322. <serve>C {                /* Receive Host command */
  323.     if (!en_hos) {
  324.     errpkt((CHAR *)"REMOTE HOST disabled");
  325.     SERVE;
  326.     } else {
  327.     srvptr = srvcmd;        /* Point to command buffer */
  328.     decode(rdatap,putsrv,0);    /* Decode command packet into it */
  329.     putsrv('\0');            /* Null-terminate */
  330.     nakstate = 0;            /* Now sending, not receiving */
  331.     if (syscmd((char *)srvcmd,"")) { /* Try to execute the command */
  332.         what = W_REMO;        /* Doing a REMOTE command. */
  333.         if (timint < 1)
  334.           timint = chktimo(rtimo,timef); /* Switch to per-packet timer */
  335.         BEGIN ssinit;        /* If OK, send back its output */
  336.     } else {            /* Otherwise */
  337.         errpkt((CHAR *)"Can't do system command"); /* report error */
  338.         SERVE;            /* & go back to server command wait */
  339.     }
  340.     }
  341. }
  342.  
  343. <serve>q {                /* User typed Ctrl-C... */
  344.     if (!en_fin) {
  345.     errpkt((CHAR *)"QUIT disabled");
  346.     SERVE;
  347.     } else {
  348.     success = 0; QUIT;
  349.     }
  350. }
  351.  
  352. <serve>N {                /* Server got a NAK in command-wait */
  353.     errpkt((CHAR *)"Did you say RECEIVE instead of GET?");
  354.     SERVE;
  355. }
  356.  
  357. <serve>. {                /* Any other command in this state */
  358.     if (c != ('E' - SP) && c != ('Y' - SP)) /* except E and Y packets. */
  359.       errpkt((CHAR *)"Unimplemented server function");
  360.     /* If we answer an E with an E, we get an infinite loop. */
  361.     /* A Y (ACK) can show up here if we sent back a short-form reply to */
  362.     /* a G packet and it was echoed.  ACKs can be safely ignored here. */
  363.     SERVE;                /* Go back to server command wait. */
  364. }
  365.  
  366. <generic>C {                /* Got REMOTE CWD command */
  367.     if (!en_cwd) {
  368.     errpkt((CHAR *)"REMOTE CD disabled");
  369.     SERVE;
  370.     } else {
  371.     if (!cwd((char *)(srvcmd+1))) errpkt((CHAR *)"Can't change directory");
  372.     SERVE;                /* Back to server command wait */
  373.     }
  374. }
  375.  
  376. <generic>A {                /* Got REMOTE PWD command */
  377.     if (!en_cwd) {
  378.     errpkt((CHAR *)"REMOTE CD disabled");
  379.     SERVE;
  380.     } else {
  381.     if (encstr((CHAR *)zgtdir()) > -1) /* Get & encode current directory */
  382.       ack1(data);            /* If it fits, send it back in ACK */
  383.     SERVE;                /* Back to server command wait */
  384.     }
  385. }
  386.  
  387. <generic>D {                /* REMOTE DIRECTORY command */
  388.     char *n2;
  389.     if (!en_dir) {            /* If DIR is disabled, */
  390.     errpkt((CHAR *)"REMOTE DIRECTORY disabled"); /* refuse. */
  391.     SERVE;
  392.     } else {                /* DIR is enabled. */
  393.     if (!en_cwd) {            /* But if CWD is disabled */
  394.         zstrip((char *)(srvcmd+2),&n2); /* and they included a pathname, */
  395.         if (strcmp((char *)(srvcmd+2),n2)) { /* refuse. */
  396.         errpkt((CHAR *)"Access denied");
  397.         SERVE;            /* Remember, this is not a goto! */
  398.         }
  399.     }    
  400.     if (state == generic) {            /* It's OK to go ahead. */
  401.         n2 = (*(srvcmd+2)) ? DIRCMD : DIRCM2;
  402.         if (syscmd(n2,(char *)(srvcmd+2)))  /* If it can be done */
  403.           BEGIN ssinit;            /* send the results back */
  404.         else {                /* otherwise */
  405.         errpkt((CHAR *)"Can't list directory"); /* report failure, */
  406.         SERVE;            /* return to server command wait */
  407.         }
  408.     }
  409.     }
  410. }
  411.  
  412. <generic>E {                /* REMOTE DELETE (Erase) command */
  413.     char *n2;
  414.     if (!en_del) {
  415.     errpkt((CHAR *)"REMOTE DELETE disabled");    
  416.     SERVE;
  417.     } else {
  418.     if (!en_cwd) {            /* But if CWD is disabled */
  419.         zstrip((char *)(srvcmd+2),&n2); /* and they included a pathname, */
  420.         if (strcmp((char *)(srvcmd+2),n2)) { /* refuse. */
  421.         errpkt((CHAR *)"Access denied");
  422.         SERVE;            /* Remember, this is not a goto! */
  423.         }
  424.     }    
  425.     if (state == generic) {            /* It's OK to go ahead. */
  426.         if (syscmd(DELCMD,(char *)(srvcmd+2))) /* Try to do it */
  427.           BEGIN ssinit;            /* If OK send results back */
  428.         else {                /* otherwise */
  429.         errpkt((CHAR *)"Can't remove file"); /* report failure */
  430.         SERVE;            /* & return to server command wait */
  431.         }
  432.     }
  433.     }
  434. }
  435.  
  436. <generic>F {                /* FINISH */
  437.     if (!en_fin) {
  438.     errpkt((CHAR *)"FINISH disabled");    
  439.     SERVE;
  440.     } else {
  441.     ack();                /* Acknowledge */
  442.     screen(SCR_TC,0,0L,"");        /* Display */
  443.     return(0);            /* Done */
  444.     }
  445. }
  446.  
  447. <generic>L {                /* BYE (LOGOUT) */
  448.     if (!en_bye) {
  449.     errpkt((CHAR *)"BYE disabled");    
  450.     SERVE;
  451.     } else {
  452.     ack();                /* Acknowledge */
  453.     ttres();            /* Reset the terminal */
  454.     screen(SCR_TC,0,0L,"");        /* Display */
  455.     doclean();            /* Clean up files, etc */
  456. #ifdef DEBUG
  457.     debug(F100,"C-Kermit BYE","",0);
  458.     zclose(ZDFILE);
  459. #endif /* DEBUG */
  460.     return(zkself());        /* Try to log self out */
  461.     }
  462. }
  463.  
  464. <generic>H {                /* REMOTE HELP */
  465.     extern char * hlptxt;
  466.     if (sndhlp(hlptxt)) BEGIN ssinit;    /* Try to send it */
  467.     else {                /* If not ok, */
  468.     errpkt((CHAR *)"Can't send help"); /* send error message instead */
  469.     SERVE;                /* and return to server command wait */
  470.     }
  471. }
  472.  
  473. <generic>S {                /* REMOTE SET */
  474.     if (!en_set) {
  475.     errpkt((CHAR *)"REMOTE SET disabled");
  476.     SERVE;
  477.     } else {
  478.     if (remset((char *)(srvcmd+1)))    /* Try to do what they ask */
  479.       ack();            /* If OK, then acknowledge */
  480.     else                /* Otherwise */
  481.       errpkt((CHAR *)"Unknown REMOTE SET parameter"); /* give error msg */
  482.     SERVE;                /* Return to server command wait */
  483.     }
  484. }
  485.  
  486. <generic>T {                /* REMOTE TYPE */
  487.     char *n2;
  488.     if (!en_typ) {
  489.     errpkt((CHAR *)"REMOTE TYPE disabled");
  490.     SERVE;
  491.     } else {
  492.     if (!en_cwd) {            /* But if CWD is disabled */
  493.         zstrip((char *)(srvcmd+2),&n2); /* and they included a pathname, */
  494.         if (strcmp((char *)(srvcmd+2),n2)) { /* refuse. */
  495.         errpkt((CHAR *)"Access denied");
  496.         SERVE;            /* Remember, this is not a goto! */
  497.         }
  498.     }    
  499.     if (state == generic) {            /* It's OK to go ahead. */
  500.         if (syscmd(TYPCMD,(char *)(srvcmd+2))) /* Try */
  501.           BEGIN ssinit;            /* OK */
  502.         else {                /* not OK */
  503.         errpkt((CHAR *)"Can't type file"); /* give error message */
  504.         SERVE;            /* wait for next server command */
  505.         }
  506.     }
  507.     }
  508. }
  509.  
  510. <generic>U {                /* REMOTE SPACE */
  511.     if (!en_spa) {
  512.     errpkt((CHAR *)"REMOTE SPACE disabled");
  513.     SERVE;
  514.     } else {
  515.     x = srvcmd[1];            /* Get area to check */
  516.     x = ((x == '\0') || (x == SP)
  517. #ifdef OS2
  518.          || (x == '!') || (srvcmd[3] == ':')
  519. #endif /* OS2 */
  520.          );
  521.     if (!x && !en_cwd) {        /* If CWD disabled and they gave */
  522.         errpkt((CHAR *)"Access denied"); /* a non-default area, */
  523.         SERVE;            /* refuse. */
  524.     } else {
  525. #ifdef OS2
  526. _PROTOTYP(int sndspace,(int));
  527.         if (sndspace(x ? toupper(srvcmd[2]) : 0))
  528.           BEGIN ssinit;        /* Try to send it */
  529.         else {            /* If not ok, */
  530.         errpkt((CHAR *)"Can't send space"); /* send error message */
  531.         SERVE;            /* and return to server command wait */
  532.         }
  533. #else
  534.         x = (x ? syscmd(SPACMD,"") : syscmd(SPACM2,(char *)(srvcmd+2)));
  535.         if (x) {                /* If we got the info */
  536.         BEGIN ssinit;            /* send it */
  537.         } else {                /* otherwise */
  538.         errpkt((CHAR *)"Can't check space"); /* send error message */
  539.         SERVE;            /* and await next server command */
  540.         }
  541. #endif /* OS2 */
  542.     }
  543.     }
  544. }
  545.  
  546. <generic>W {                /* REMOTE WHO */
  547.     if (!en_who) {
  548.     errpkt((CHAR *)"REMOTE WHO disabled");
  549.     SERVE;
  550.     } else {
  551.     if (syscmd(WHOCMD,(char *)(srvcmd+2))) /* The now-familiar scenario. */
  552.       BEGIN ssinit;
  553.     else {
  554.         errpkt((CHAR *)"Can't do who command");
  555.         SERVE;
  556.     }
  557.     }
  558. }
  559.  
  560. <generic>V {                /* Variable query or set */
  561. #ifndef NOSPL
  562. _PROTOTYP( int addmac, (char *, char *) );
  563. _PROTOTYP( int zzstring, (char *, char **, int *) );
  564.     char c;
  565.     c = *(srvcmd+2);            /* Q = Query, S = Set */
  566.     if (c == 'Q') {            /* Query */
  567.     if (!en_que) {            /* Security */
  568.         errpkt((CHAR *)"REMOTE QUERY disabled");
  569.         SERVE;
  570.     } else {            /* Query allowed */
  571.         int n; char *p, *q;
  572.         qbufp = querybuf;        /* Wipe out old stuff */
  573.         qbufn = 0;
  574.         querybuf[0] = NUL;
  575.         p = (char *) srvcmd + 3;    /* Pointer for making wrapper */
  576.         n = strlen((char *)srvcmd);    /* Position of end */
  577.         c = *(srvcmd+4);        /* Which type of variable */
  578.  
  579.         if (*(srvcmd+6) == CMDQ) {    /* Starts with command quote? */
  580.         p = (char *) srvcmd + 6; /* Take it literally */
  581.         } else {            /* They played by the rules */
  582.         *(srvcmd+3) = CMDQ;    /* Stuff wrapping into buffer */
  583.         *(srvcmd+5) = '(';    /* around variable name */
  584.         *(srvcmd+n) = ')';
  585.         *(srvcmd+n+1) = NUL;
  586.         if (c == 'K') {        /* Kermit variable */
  587.             *(srvcmd+4) = 'v';    /*  so make it \v(...) */
  588.         } else if (c == 'S') {    /* System variable */
  589.             *(srvcmd+4) = '$';    /*  so it's \$(...) */
  590.         } else if (c == 'G') {    /* Non-\ Global variable */
  591.             *(srvcmd+4) = 'm';    /*  so wrap it in \m(...) */
  592.         }
  593.         }                /* Now evaluate it */
  594.         n = QBUFL;            /* Max length */
  595.         q = querybuf;        /* Where to put it */
  596.         if (zzstring(p,&q,&n) < 0) {
  597.         errpkt((n > 0) ? (CHAR *)"Can't get value"
  598.                        : (CHAR *)"Value too long"
  599.                );
  600.         SERVE;
  601.         } else {
  602.         if (encstr((CHAR *)querybuf) > -1) { /* Encode it */
  603.             ack1(data);        /* If it fits, send it back in ACK */
  604.             SERVE;
  605.         } else if (sndhlp(querybuf)) { /* Long form response */
  606.             BEGIN ssinit;
  607.         } else {        /* sndhlp() fails */
  608.             errpkt((CHAR *)"Can't send value");
  609.             SERVE;
  610.         }
  611.         }
  612.     }
  613.     } else if (c == 'S') {        /* Set (assign) */
  614.     if (!en_asg) {            /* Security */
  615.         errpkt((CHAR *)"REMOTE ASSIGN disabled");
  616.         SERVE;
  617.     } else {            /* OK */
  618.         int n;
  619.         n = xunchar(*(srvcmd+3));    /* Length of name */
  620.         n = 3 + n + 1;        /* Position of length of value */
  621.         *(srvcmd+n) = NUL;        /* Don't need it */
  622.         if (addmac((char *)(srvcmd+4),(char *)(srvcmd+n+1)) < 0)
  623.           errpkt((CHAR *)"REMOTE ASSIGN failed");
  624.         else
  625.           ack();
  626.         SERVE;
  627.     }
  628.     } else {
  629.     errpkt((CHAR *)"Badly formed server command");
  630.     SERVE;
  631.     }
  632. #else
  633.     errpkt((CHAR *)"Variable query/set not available");
  634.     SERVE;
  635. #endif /* NOSPL */
  636. }
  637.  
  638. <generic>q {
  639.     if (!en_fin) {            /* Ctrl-C typed */
  640.     errpkt((CHAR *)"QUIT disabled");
  641.     SERVE;
  642.     } else {
  643.     success = 0; QUIT;
  644.     }
  645. }
  646.  
  647. <generic>. {                /* Anything else in this state... */
  648.     errpkt((CHAR *)"Unimplemented REMOTE command"); /* Complain */
  649.     SERVE;                /* and return to server command wait */
  650. }
  651.  
  652. <rgen>Y {                /* Short-Form reply */
  653. #ifndef NOSPL
  654.     if (query) {            /* If to query, */
  655.     qbufp = querybuf;        /*  initialize query response buffer */
  656.     qbufn = 0;
  657.     querybuf[0] = NUL;
  658.     }
  659. #endif /* NOSPL */
  660.     decode(rdatap,puttrm,0);        /* Text is in ACK Data field */
  661.     if (rdatap)                /* If we had data */
  662.       if (*rdatap)
  663.      conoll("");            /* Then add a CRLF */
  664.     RESUME;
  665. }
  666.  
  667. <rgen,rfile>F {                /* File header */
  668.     xflg = 0;                /* Not screen data */
  669.     if (!rcvfil(filnam)) {        /* Figure out local filename */
  670.     errpkt((CHAR *)rf_err);        /* Trouble */
  671.     screen(SCR_EM,0,0L,rf_err);
  672.     RESUME;
  673.     } else {                /* OK to receive */
  674.     encstr((CHAR *)filnam);        /* Encode the local filename */
  675.     ack1(data);            /* Send it back in ACK */
  676.     initattr(&iattr);        /* Clear file attribute structure */
  677.     if (window(wslotn) < 0) {    /* Allocate negotiated window slots */
  678.         errpkt((CHAR *)"Can't open window");
  679.         RESUME;
  680.     }
  681.     BEGIN rattr;            /* Now expect Attribute packets */
  682.     }
  683. }
  684.  
  685. <rgen,rfile>X {                /* X-packet instead of file header */
  686.     xflg = 1;                /* Screen data */
  687.     ack();                /* Acknowledge the X-packet */
  688.     initattr(&iattr);            /* Initialize attribute structure */
  689.     if (window(wslotn) < 0) {        /* allocate negotiated window slots */
  690.     errpkt((CHAR *)"Can't open window");
  691.     RESUME;
  692.     }
  693. #ifndef NOSPL
  694.     if (query) {            /* If this is the response to */
  695.     qbufp = querybuf;        /* a query that we sent, initialize */
  696.     qbufn = 0;            /* the response buffer */
  697.     querybuf[0] = NUL;
  698.     }
  699. #endif /* NOSPL */
  700.     what = W_REMO;            /* we're doing a REMOTE command */
  701.     BEGIN rattr;            /* Expect Attribute packets */
  702. }
  703.  
  704. <rattr>A {                /* Attribute packet */
  705.     if (gattr(rdatap,&iattr) == 0) {    /* Read into attribute structure */
  706. #ifdef CK_RESEND
  707.     ack1((CHAR *)iattr.reply.val);    /* Reply with data */
  708. #else
  709.     ack();                /* If OK, acknowledge */
  710. #endif /* CK_RESEND */
  711.     } else {                /* Otherwise */
  712.     ack1((CHAR *)iattr.reply.val);    /* refuse to accept the file */
  713.     screen(SCR_ST,ST_REFU,0L,getreason(iattr.reply.val)); /* give reason */
  714.     }
  715. }
  716.  
  717. <rattr>D {                /* First data packet */
  718.     if (discard) {            /* if we're discarding the file */
  719.     ack1((CHAR *)"X");        /* just ack the data like this. */
  720.     BEGIN rdata;            /* and wait for more data packets. */
  721.     } else {                /* Not discarding. */
  722.     rf_err = "Can't open file";
  723.     if (xflg) {            /* If screen data */
  724.         x = opent(&iattr);        /* "open" the screen */
  725.     } else                /* otherwise */
  726.       x = opena(filnam,&iattr);    /* open the file, with attributes */
  727.     if (x) {            /* If file was opened ok */
  728.         if (decode(rdatap, 
  729. #ifndef NOSPL
  730.                query ? puttrm : 
  731. #endif /* NOSPL */
  732.                putfil, 1) < 0) {
  733.  
  734.         errpkt((CHAR *)"Error writing data");
  735.         RESUME;
  736.         }
  737.         ack();            /* acknowledge it */
  738.         BEGIN rdata;        /* and switch to receive-data state */
  739.     } else {            /* otherwise */
  740.         errpkt((CHAR *) rf_err);    /* send error message */
  741.         RESUME;            /* and quit. */
  742.     }
  743.     }
  744. }
  745.  
  746. <rfile>B {                /* EOT, no more files */
  747.     ack();                /* Acknowledge */
  748.     tsecs = gtimer();            /* Get timing for statistics */
  749.     reot();                /* Do EOT things */
  750.     RESUME;                /* and quit */
  751. }
  752.  
  753. <rdata>D {                /* Data packet */
  754.     if (cxseen || discard)        /* If file interrupt */
  755.       ack1((CHAR *)"X");        /* put "X" in ACK */
  756.     else if (czseen)            /* If file-group interrupt */
  757.       ack1((CHAR *)"Z");        /* put "Z" in ACK */
  758.     else if (decode(rdatap, 
  759. #ifndef NOSPL
  760.                query ? puttrm : 
  761. #endif /* NOSPL */
  762.                putfil, 1) < 0) {
  763.     errpkt((CHAR *)"Error writing data"); /* If failure, */
  764.     clsof(!keep);            /*   Close & keep/discard the file */
  765.     RESUME;                /* Send ACK only after data */
  766.     } else ack();            /* written to file OK. */
  767. }
  768.  
  769. <rattr>Z {                /* EOF immediately after A-Packet. */
  770.     rf_err = "Can't create file";
  771.     if (discard) {            /* Discarding a real file... */
  772.     x = 1;
  773.     } else if (xflg) {            /* Zero-length file. If screen data */
  774.     x = opent(&iattr);        /* "open" the screen */
  775.     } else {                /* otherwise */
  776.     x = opena(filnam,&iattr);    /* open the file, with attributes. */
  777.     }
  778.     if (!x || reof(filnam, &iattr) < 0) { /* Now close & dispose of the file */
  779.     errpkt((CHAR *) rf_err);    /* If problem, send error msg */
  780.     RESUME;                /* and quit */
  781.     } else {                /* otherwise */
  782.     ack();                /* acknowledge the EOF packet */
  783.     BEGIN rfile;            /* and await another file */
  784.     }
  785. }
  786.  
  787. <rdata>Z {                /* End Of File (EOF) Packet */
  788. /*  wslots = 1;    */            /* Window size back to 1 */
  789. #ifndef COHERENT
  790. /*
  791.   Coherent compiler blows up on this switch() statement.
  792. */
  793.     x = reof(filnam, &iattr);        /* Handle the EOF packet */
  794.     switch (x) {            /* reof() sets the success flag */
  795.       case -3:                /* If problem, send error msg */
  796.     errpkt((CHAR *)"Can't print file"); /* Fatal */
  797.         RESUME;
  798.     break;
  799.       case -2:
  800.     errpkt((CHAR *)"Can't mail file"); /* Fatal */
  801.         RESUME;
  802.     break;
  803.       case 2:
  804.       case 3:
  805.     screen(SCR_EM,0,0L,"Can't delete temp file"); /* Not fatal */
  806.         RESUME;
  807.     break;
  808.       default:
  809.     if (x < 0) {            /* Fatal */
  810.         errpkt((CHAR *)"Can't close file");
  811.         RESUME;
  812.     } else {            /* Success */
  813. #ifndef NOSPL
  814.         if (query)            /* Query reponses generally */
  815.           conoll("");        /* don't have line terminators */
  816. #endif /* NOSPL */
  817.         ack();            /* Acknowledge the EOF packet */
  818.         BEGIN rfile;        /* and await another file */
  819.     }
  820.     }
  821. #else
  822.     if (reof(filnam, &iattr) < 0) {    /* Close and dispose of the file */
  823.     errpkt((CHAR *)"Error at end of file");
  824.     RESUME;
  825.     } else {                /* reof() sets success flag */
  826.     ack();
  827.     BEGIN rfile;
  828.     }
  829. #endif /* COHERENT */
  830. }
  831.  
  832. <ssinit>Y {                /* ACK for Send-Init */
  833.     spar(rdatap);            /* set parameters from it */
  834.     bctu = bctr;            /* switch to agreed-upon block check */
  835.     bctl = (bctu == 4) ? 2 : bctu;    /* Set block-check length */
  836. #ifdef CK_RESEND
  837.     if ((sendmode == SM_RESEND) && (!atcapu || !rscapu)) { /* RESEND */
  838.     errpkt((CHAR *) "RESEND capabilities not negotiated");
  839.     ermsg("RESEND capabilities not negotiated");
  840.     RESUME;
  841.     } else {
  842. #endif /* CK_RESEND */
  843.     what = W_SEND;            /* Remember we're sending */
  844.     x = sfile(xflg);        /* Send X or F header packet */
  845.     if (x) {            /* If the packet was sent OK */
  846.         resetc();            /* reset per-transaction counters */
  847.         rtimer();            /* reset timers */
  848.         BEGIN ssfile;        /* and switch to receive-file state */
  849.     } else {            /* otherwise send error msg & quit */
  850.         s = xflg ? "Can't execute command" : "Can't open file";
  851.         errpkt((CHAR *)s);
  852.         RESUME;
  853.     }
  854. #ifdef CK_RESEND
  855.     }
  856. #endif /* CK_RESEND */
  857. }
  858.  
  859. /*
  860.  These states are necessary to handle the case where we get a server command
  861.  packet (R, G, or C) reply with an S packet, but the client retransmits the 
  862.  command packet.  The input() function doesn't catch this because the packet 
  863.  number is still zero.
  864. */
  865. <ssinit>R {                /* R packet was retransmitted. */
  866.     xsinit();                /* Resend packet 0 */
  867. }
  868.  
  869. <ssinit>G {                /* Same deal if G packet comes again */
  870.     xsinit();
  871. }
  872.  
  873. <ssinit>C {                /* Same deal if C packet comes again */
  874.     xsinit();
  875. }
  876.  
  877. <ssfile>Y {                /* ACK for F packet */
  878.     srvptr = srvcmd;            /* Point to string buffer */
  879.     decode(rdatap,putsrv,0);        /* Decode data field, if any */
  880.     putsrv('\0');            /* Terminate with null */
  881.     ffc = 0L;                /* Reset file byte counter */
  882.     if (*srvcmd) {            /* If remote name was recorded */
  883.     if (fdispla == XYFD_C) screen(SCR_AN,0,0L,(char *)srvcmd);
  884.     tlog(F110," remote name:",(char *) srvcmd,0L); /* Transaction log. */
  885.     }
  886.     if (atcapu) {            /* If attributes are to be used */
  887.     if (sattr(xflg | stdinf) < 0) {    /* set and send them */
  888.         errpkt((CHAR *)"Can't send attributes"); /* if problem, say so */
  889.         RESUME;                 /* and quit */
  890.     } else BEGIN ssattr;        /* if ok, switch to attribute state */
  891.     } else {
  892.     if (window(wslotn) < 0) {
  893.         errpkt((CHAR *)"Can't open window");
  894.         RESUME;
  895.     }
  896.     if (sdata() < 0) {        /* No attributes, send data */
  897.         clsif();            /* If not ok, close input file, */
  898.         window(1);            /* put window size back to 1, */
  899.         seof((CHAR *)"");        /* send EOF packet, */
  900.         BEGIN sseof;        /* and switch to EOF state. */
  901.     } else BEGIN ssdata;        /* All ok, switch to send-data state */
  902.     }
  903. }
  904.  
  905. <ssattr>Y {                /* Got ACK to A packet */
  906.     ffc = 0L;                /* Reset file byte counter */
  907.     if (rsattr(rdatap) < 0) {        /* Was the file refused? */
  908.     discard = 1;            /* Set the discard flag */
  909.     clsif();            /* Close the file */
  910.     sxeof((CHAR *)"D");        /* send EOF with "discard" code */
  911.     BEGIN sseof;            /* switch to send-EOF state */
  912.     } else {
  913.     if (window(wslotn) < 0) {    /* Allocate negotiated window slots */
  914.         errpkt((CHAR *)"Can't open window");
  915.         RESUME;
  916.     }
  917.     if (sdata() < 0) {        /* File accepted, send data */
  918.         clsif();            /* If problem, close input file */
  919.         window(1);            /* Window size back to 1... */
  920.         seof((CHAR *)"");        /* send EOF packet */
  921.         BEGIN sseof;        /* and switch to send-EOF state. */
  922.     } else {            /* All ok, enter send-data state. */
  923.         BEGIN ssdata;
  924.     }
  925.     }
  926. }
  927.  
  928. <ssdata>Y {                /* Got ACK to Data packet */
  929.     canned(rdatap);            /* Check if file transfer cancelled */
  930.     if (sdata() < 0) {            /* Try to send next data */
  931.     clsif();            /* If no more data, close file */
  932.     window(1);            /* Window size back to 1... */
  933.     if (cxseen || czseen)        /* If interrupted */
  934.       seof((CHAR *)"D");        /* send special EOF packet */
  935.     else seof((CHAR *)"");        /* Otherwise regular EOF packet */
  936.     BEGIN sseof;            /* And enter send-eof state */
  937.     }
  938. }
  939.  
  940. <sseof>Y {                /* Got ACK to EOF */
  941.     success = (cxseen == 0 && czseen == 0); /* Transfer status... */
  942.     if (success && rejection > 0)        /* If rejected, succeed if */
  943.       if (rejection != '#' &&            /* reason was date */
  944.       rejection != 1 && rejection != '?') /* or name; */
  945.     success = 0;                /* fail otherwise. */
  946.     cxseen = 0;                /* This goes back to zero. */
  947.     if (gnfile() > 0) {            /* Any more files to send? */
  948.     if (sfile(xflg))        /* Yes, try to send next file header */
  949.       BEGIN ssfile;            /* if ok, enter send-file state */
  950.     else {                /* otherwise */
  951.         errpkt((CHAR *)"Can't open file");    /* send error message */
  952.         RESUME;            /* and quit */
  953.     }
  954.     } else {                /* No next file */
  955.     tsecs = gtimer();        /* get statistics timers */
  956.     seot();                /* send EOT packet */
  957.     BEGIN sseot;            /* enter send-eot state */
  958.     }
  959. }
  960.  
  961. <sseot>Y {                /* Got ACK to EOT */
  962.     RESUME;                /* All done, just quit */
  963. }
  964.  
  965. E {                    /* Got Error packet, in any state */
  966.     char *s = "";
  967.     if (pktmsg)                /* Or we sent one. */
  968.       if (*pktmsg)            /* If so, this was the message. */
  969.     s = (char *)pktmsg;
  970.     if (!*s)                /* We received an Error packet */
  971.       s = (char *)rdatap;        /* with this message. */
  972.     if (!*s)                /* Hopefully we'll never see this. */
  973.       s = "Unknown error";
  974.     ermsg(s);                /* Issue the message. */
  975.     success = 0;            /* For IF SUCCESS/FAIL. */
  976.     debug(F111,"ckcpro.w sstate at E pkt",s,sstate);
  977.     x = quiet; quiet = 1;        /* Close files silently, */
  978.     clsif(); clsof(1);             /* discarding any output file. */
  979.     tsecs = gtimer();            /* Get timers */
  980.     quiet = x;                /* restore quiet state */
  981. /*
  982.   If we are executing commands from a command file or macro, let the command
  983.   file or macro decide whether to exit, based on SET { TAKE, MACRO } ERROR.
  984. */
  985.     if (
  986. #ifndef NOSPL
  987.     cmdlvl == 0
  988. #else
  989.     tlevel < 0
  990. #endif /* NOSPL */
  991.     )
  992.       if (backgrd && !server)
  993.     fatal("Protocol error");
  994.     xitsta |= what;            /* Save this for doexit(). */
  995.     RESUME;
  996. }
  997.  
  998. q { success = 0; QUIT; }        /* Ctrl-C interrupt during packets. */
  999.  
  1000. . {                    /* Anything not accounted for above */
  1001.     errpkt((CHAR *)"Unexpected packet type"); /* Give error message */
  1002.     xitsta |= what;            /* Save this for doexit(). */
  1003.     RESUME;                /* and quit */
  1004. }
  1005. %%
  1006.  
  1007. /*  P R O T O  --  Protocol entry function  */
  1008.  
  1009. VOID
  1010. proto() {
  1011.  
  1012.     int x;
  1013.     long lx;
  1014.  
  1015. /* Set up the communication line for file transfer. */
  1016.  
  1017.     if (local && (speed < 0L) && (network == 0)) {
  1018.     screen(SCR_EM,0,0L,"Sorry, you must 'set speed' first");
  1019.     return;
  1020.     }
  1021.     x = -1;
  1022.     if (ttopen(ttname,&x,mdmtyp,cdtimo) < 0) {
  1023.     debug(F111,"failed: proto ttopen local",ttname,local);
  1024.     screen(SCR_EM,0,0L,"Can't open line");
  1025.     return;
  1026.     }
  1027.     if (x > -1) local = x;
  1028.     debug(F111,"proto ttopen local",ttname,local);
  1029.  
  1030.     lx = (local && !network) ? speed : -1;
  1031. #ifdef NETCONN
  1032. #ifdef CK_SPEED
  1033. /*
  1034.   If we are a TELNET client, force quoting of IAC.
  1035.   Note hardwired "1" rather than NP_TELNET symbol, so we don't have
  1036.   to schlurp in ckcnet.h.
  1037. */
  1038.     if (network && ttnproto == 1) {
  1039.     ctlp[255] = ctlp[CR] = 1;
  1040.     if (parity == 'e' || parity == 'm') ctlp[127] = 1;
  1041.     }
  1042. #endif /* CK_SPEED */
  1043. #endif /* NETCONN */
  1044.     if (ttpkt(lx,flow,parity) < 0) {    /* Put line in packet mode, */
  1045.     screen(SCR_EM,0,0L,"Can't condition line");
  1046.     return;
  1047.     }
  1048.     if (!local) connoi();        /* No console interrupts if remote */
  1049.  
  1050.     if (sstate == 'x') {        /* If entering server mode, */
  1051.     server = 1;            /* set flag, */
  1052.     debug(F101,"server backgrd","",backgrd);
  1053.     debug(F101,"server quiet","",quiet);
  1054.     if (!quiet && !backgrd) {
  1055.             debug(F100,"SHOULD NOT SEE THIS IF IN BACKGROUND!","",0);
  1056.         if (!local)    {        /* and issue appropriate message. */
  1057.             conoll(srvtxt);
  1058.         conoll("KERMIT READY TO SERVE...");
  1059.         } else {
  1060.             conol("Entering server mode on ");
  1061.         conoll(ttname);
  1062.         conoll("Type Ctrl-C to quit.");
  1063.         if (srvdis) intmsg(-1L);
  1064.         }
  1065.     }
  1066.     } else server = 0;
  1067. #ifdef VMS
  1068.     if (!quiet && !backgrd)    /* So message doesn't overwrite prompt */
  1069.       conoll("");
  1070.     if (local) conres();       /* So Ctrl-C will work */
  1071. #endif /* VMS */
  1072. /*
  1073.   If in remote mode, not shushed, not in background, and at top command level,
  1074.   issue a helpful message telling what to do...
  1075. */
  1076.     if (!local && !quiet && !backgrd) {
  1077.     if (sstate == 'v') {
  1078.         conoll("Return to your local Kermit and give a SEND command.");
  1079.         conoll("");
  1080.         conoll("KERMIT READY TO RECEIVE...");
  1081.     } else if (sstate == 's') {
  1082.         conoll("Return to your local Kermit and give a RECEIVE command.");
  1083.         conoll("");
  1084.         conoll("KERMIT READY TO SEND...");
  1085.     } else if ( sstate == 'g' || sstate == 'r' || sstate == 'c' ) {
  1086.         conoll("Return to your local Kermit and give a SERVER command.");
  1087.         conoll("");
  1088.         conoll((sstate == 'r') ?
  1089.            "KERMIT READY TO GET..." :
  1090.            "KERMIT READY TO SEND SERVER COMMAND...");
  1091.     }
  1092.     }
  1093. #ifdef COMMENT
  1094.     if (!local) sleep(1);
  1095. #endif /* COMMENT */
  1096. /*
  1097.   The 'wart()' function is generated by the wart program.  It gets a
  1098.   character from the input() routine and then based on that character and
  1099.   the current state, selects the appropriate action, according to the state
  1100.   table above, which is transformed by the wart program into a big case
  1101.   statement.  The function is active for one transaction.
  1102. */
  1103.     rtimer();                /* Reset elapsed-time timer */
  1104.     resetc();                /* & other per-transaction counters. */
  1105.     wart();                /* Enter the state table switcher. */
  1106.     
  1107.     if (server) {            /* Back from packet protocol. */
  1108.         if (!quiet && !backgrd) {    /* Give appropriate message */
  1109.         conoll("");
  1110.         conoll("C-Kermit server done");
  1111.         }
  1112.     }
  1113. /*
  1114.   Note: the following is necessary in case we have just done a remote-mode
  1115.   file transfer, in which case the controlling terminal modes have been
  1116.   changed by ttpkt().  In particular, special characters like Ctrl-C and
  1117.   Ctrl-\ might have been turned off (see ttpkt).  So this call to ttres() is
  1118.   essential.
  1119. */
  1120. #ifndef OS2
  1121.     if (!local)
  1122. #endif /* OS2 */
  1123.       ttres();                /* Reset the communication device */
  1124.     screen(SCR_TC,0,0L,"");        /* Transaction complete */
  1125.     server = 0;                /* Not a server any more */
  1126. }
  1127.