1 /* ***********************************************************
   2    *                                                         *
   3    * Copyright, (C) Honeywell Bull Inc., 1987                *
   4    *                                                         *
   5    * Copyright, (C) Honeywell Information Systems Inc., 1986 *
   6    *                                                         *
   7    *********************************************************** */
   8 
   9 /* HISTORY COMMENTS:
  10   1) change(86-01-27,Hoover), approve(87-07-13,MCR7580),
  11      audit(87-07-13,Leskiw), install(87-08-07,MR12.1-1072):
  12      Created.
  13   2) change(86-09-15,Flegel), approve(87-07-13,MCR7580),
  14      audit(87-07-13,Leskiw), install(87-08-07,MR12.1-1072):
  15      Changed check-sum from character addition to
  16      CRC-6.
  17   3) change(86-09-24,Flegel), approve(87-07-13,MCR7580),
  18      audit(87-07-13,Leskiw), install(87-08-07,MR12.1-1072):
  19      Changed sndpkt to allow only DisConnect packets
  20      to be sent when a disconnect is in progress.
  21   4) change(86-10-15,Flegel), approve(87-07-13,MCR7580),
  22      audit(87-07-13,Leskiw), install(87-08-07,MR12.1-1072):
  23      Added FastDis for fast (non-confirmed)
  24      disconnects.
  25   5) change(86-12-25,Flegel), approve(87-07-13,MCR7580),
  26      audit(87-07-13,Leskiw), install(87-08-07,MR12.1-1072):
  27      Moved setting of the packetize_flag from
  28      get_inbuff to prsrst to ensure that it is set only when entire protocol is
  29      set.
  30                                                    END HISTORY COMMENTS */
  31 
  32 /* : PROGRAM FUNCTION
  33 
  34 MIO is the PC module of MOWSE which controls packet communications between
  35 Multics and the PC in order to guarantee error free communications.
  36 */
  37 
  38 /* : NOTES
  39 
  40 The following is a description of a packet:
  41 
  42     --------------------------------------------------
  43    | SOP | TYPE | ... {data} ... | LENGTH | CRC | EOP |
  44     --------------------------------------------------
  45 
  46 Supervisory packets (those which do not carry data) all are of length 5 and
  47 carry no data (ACK, NAK, RST, BRK, and there associated types)
  48 */
  49 
  50 #include <ws_error.h>
  51 
  52 /* Special constants */
  53 #define TIME_OUT       1               /* Disconnect timed out */
  54 #define DISCONNECT_OUT 2               /* Disconnect completed */
  55 
  56 /* logical Constants */
  57 #define False   0
  58 #define True    1
  59 
  60 /* ASCII Control Characters */
  61 #define NULL         0
  62 #define CR           13
  63 #define ESC          27
  64 #define LF           10
  65 #define SI           15
  66 #define SO           14
  67 #define SOH          1
  68 #define MIN_ASCII    32
  69 #define REV_VIDEO    0200
  70 #define Null_Convert 20
  71 #define MASK_ALL     0377
  72 #define MASK_SIX     077
  73 
  74 /* Protocol Bit-field Constants */
  75 #define ChnCnt  2                      /* Number of channels */
  76 #define SeqFld  2                      /* Number of bits in sequence field */
  77 #define SeqCnt  4                      /* 2**SeqFld */
  78 #define SeqMsk  3                      /* SeqCnt - 1 */
  79 
  80 /* Protocol Byte-field Constants */
  81 #define SOPLen          1              /* Characters in SOP field */
  82 #define TypLen          1              /* Characters in TYPE field */
  83 #define MaxDatLen       124            /* Maximum number of data chars */
  84 #define ChkLen          1              /* Characters in CHECKSUM field */
  85 #define EOPLen          1              /* Characters in EOP field */
  86 #define LenLen          1              /* Characters in Length field */
  87 #define MinPktLen (SOPLen+TypLen+LenLen+ChkLen+EOPLen) /* Minimum chars in Packet */
  88 #define MaxPktLen (MinPktLen+MaxDatLen) /* Maximum chars in Packet */
  89 
  90 /* Protocol Packet Constants */
  91 #define BG      0                      /* Background Channel */
  92 #define FG      1                      /* Foreground Channel */
  93 #define RstOff  ' '                    /* <SPACE> */
  94 #define Request 0                      /* Packet request */
  95 #define Confirm 1                      /* Packet confirmation */
  96 #define RstCnt  (Confirm+1)            /* Number of types of reset packets */
  97 #define BrkOff  (RstOff+RstCnt)        /* Break packet base */
  98 #define BrkCnt  (ChnCnt*(Confirm+1))   /* Number of types of break packet */
  99 #define DisCon  (BrkOff+(2*BG))        /* Disconnect packet */
 100 #define FGBrk   (BrkOff+(2*FG))        /* Foreground break packet */
 101 #define DatOff  (BrkOff+BrkCnt)        /* Data packet base */
 102 #define DatCnt  (ChnCnt*SeqCnt*SeqCnt) /* Number of types of data packets */
 103 #define AckOff  (38+DatCnt)            /* Ack packet base */
 104 #define AckCnt  (ChnCnt*SeqCnt)        /* Number of types of Ack packets */
 105 #define NakOff  (AckOff+AckCnt)        /* Nak packet base */
 106 #define NakCnt  (ChnCnt*SeqCnt)        /* Number of types of Nak packets */
 107 #define FastDis (NakOff+NakCnt)        /* Fast disconnect packet */
 108 
 109 /* Protocol Parameters */
 110 #define Debug           True
 111 #define RQS             2              /* 'rcvchr's receive queue size */
 112 #define RWS             3              /* Receiver's window size */
 113 #define SWS             3              /* Sender's windo size */
 114 #define LimRTmr         7              /* Limit for receiver timer */
 115 #define LimSTmr         15             /* Limit for sender timer */
 116 #define LimPTmr         30             /* Limit for pending timer */
 117 #define InitDis         1              /* PC initiated disconnection */
 118 #define RespDis         2              /* Multics initiated disconnection */
 119 #define InitRst         1              /* PC initiated reset */
 120 #define RespRst         2              /* Multics initiated reset */
 121 #define REVPOLY         051            /* bit N is coeff of x**(5-N) of CRC generator */
 122 #define INIT_CRC        63             /* Initial value for seed in calculating CRC */
 123 #define Reset_Timer_X   0              /* pending_timer index for reset resends */
 124 #define Dis_Timer_X     1              /* pending_timer index for disconnect resends */
 125 #define Break_Timer_X   2              /* pending_timer index for break resends */
 126 
 127 /* Debugging Switches */
 128 #if Debug
 129    int  dbgpkts = False;               /* show packets */
 130    int  dbgrejs = False;               /* diagnose rejects */
 131    int  dbgxchr = False;               /* show extraneous received chars */
 132 #endif
 133 
 134 /* Task Control Variables */
 135 int  apv_locked = False;               /* Approving a packet */
 136 int  ds_pending = 0;                   /* Process of disconnecting */
 137 int  ds_timeout = 0;                   /* PC initiation of disconnect timeout */
 138 int  rs_pending = 0;                   /* Process of resetting */
 139 int  br_pending = 0;                   /* Process of a FG break */
 140 int  pending_timer[3] = {0,0,0};       /* Timer on reset/disconnect/break */
 141 
 142 /* Receiver Global Variables */
 143 char r_EOP          = LF;              /* End Of Packet char */
 144 char r_ESC[3]       = {ESC, SI, SO};   /* Receiver's 3 ESCape chars */
 145 int  r_ESC_count    = 0;               /* Number of ESCape chars read in */
 146 char r_SOP          = SOH;             /* Start Of Packet char */
 147 int  r_asn[ChnCnt]  = {0, 0};          /* Acked sequence numbers */
 148 char r_esckey       = '\000';          /* Key to decoding 2nd char of esc */
 149 int  r_ignoring[ChnCnt] = {False, False}; /* Ignoring data pending resync */
 150 int  r_pktin        = 0;               /* Next free slot in receive Q */
 151 int  r_pktout       = 0;               /* Head of receive Q */
 152 int  r_psn[ChnCnt]  = {0, 0};          /* Receive packet sequence number */
 153 int  r_timer[ChnCnt] = {0, 0};         /* Time since last ack */
 154 char r_dat[ChnCnt][SeqCnt][1+MaxDatLen];  /* receive data Q */
 155 char r_pkt[RQS+1][1+MaxPktLen];        /* Receive packet */
 156 
 157 /* Sender Global Variables */
 158 char s_EOP          = LF;              /* End Of Packet Char */
 159 char s_ESC[3]       = {ESC, SI, SO};   /* Sender's 3 esc chars */
 160 char s_SOP          = SOH;             /* Start Of Packet Char */
 161 int  s_lasn[ChnCnt] = {0, 0};          /* Last Acked sequence number */
 162 int  s_nasn[ChnCnt] = {0, 0};          /* Next Ack to send */
 163 int  s_psn[ChnCnt]  = {0, 0};          /* Packet sequence number */
 164 int  s_timer[ChnCnt] = {0, 0};         /* Time since last ack */
 165 char s_escreq[256]  = {0,1,0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1,
 166                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 167                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 168                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 169                        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
 170                        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
 171                        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
 172                        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
 173                        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
 174                        1,1,1,1};       /* Chars to be esc'ed when sent */
 175 char s_dat[ChnCnt][SeqCnt][1+MaxDatLen]; /* Senders data Q */
 176 
 177 /* Current execution modes */
 178 extern int packet_mode;                /* Indicates that packet mode is on */
 179 extern int packetize_flag;             /* Indicates to TE to accumulate packets */
 180 int mowse_terminating = 0;             /* Indicates that MOWSE is terminating */
 181 
 182 /*^L*/
 183 
 184 /* : PROCEDURE FUNCTION (advtmr)
 185 
 186 Advance various timers.  If a timer reaches its limit, it is reset and the
 187 associated time-out action is performed.
 188 */
 189 
 190 advtmr()
 191 {
 192 int  chn;                              /* Channel number */
 193 int  i;
 194 char type;                             /* Type field of packet */
 195 
 196 /* : For each channel
 197      - If receive time limit exceeded then send an ack */
 198 
 199    for (chn = 0; chn < ChnCnt; chn++)
 200    {  r_timer[chn]++;
 201       if (r_timer[chn] > LimRTmr)
 202       {  r_timer[chn] = 0;
 203          if (s_nasn[chn] != s_lasn[chn])
 204             sndack(chn);
 205       }
 206 
 207 /* : - If send time limit exceeded then send an ack */
 208 
 209       s_timer[chn]++;
 210       if (s_timer[chn] > LimSTmr)
 211       {  s_timer[chn] = 0;
 212          resend(chn);
 213       }
 214    }
 215 
 216 /* : Increment the pending timer */
 217 
 218    for (i = 0; i < 3; pending_timer[i++]++);
 219 
 220 /* : If limit exceeded on reset packet, then resend */
 221 
 222    if ((rs_pending & InitRst) && (pending_timer[Reset_Timer_X] > LimPTmr))
 223    {  pending_timer[Reset_Timer_X] = 0;
 224       type = RstOff + Request;
 225       sndpkt(type,"");
 226    }
 227    if ((rs_pending & RespRst) && (pending_timer[Reset_Timer_X] > LimPTmr))
 228    {  pending_timer[Reset_Timer_X] = 0;
 229       type = RstOff + Confirm;
 230       sndpkt(type,"");
 231    }
 232 
 233 /* : If limit exceeded on disconnect packet
 234      - set timeout flag if PC initiated, else resend reply */
 235 
 236    if ((ds_pending & InitDis) && (pending_timer[Dis_Timer_X] > LimPTmr))
 237    {  ds_pending &= ~InitDis;
 238       ds_timeout = TIME_OUT;
 239    }
 240 
 241    if ((ds_pending & RespDis) && (pending_timer[Dis_Timer_X] > LimPTmr))
 242    {  pending_timer[Dis_Timer_X] = 0;
 243       type = DisCon + Confirm;
 244       sndpkt(type,"");
 245    }
 246 
 247 /* : If limit exceeded on break packet, then resend */
 248 
 249    if ((br_pending) && (pending_timer[Break_Timer_X] > LimPTmr))
 250    {  pending_timer[Break_Timer_X] = 0;
 251       type = FGBrk + Request;
 252       sndpkt(type,"");
 253    }
 254 }
 255 
 256 /*^L*/
 257 
 258 /* : PROCEDURE FUNCTION (apvpkt)
 259 
 260 Approve packet.  If packet appears valid, dispatch it to the appropriate
 261 routine; otherwise, reject it.
 262 */
 263 
 264 apvpkt (pkt)
 265 
 266 char pkt[];     /* The packet to be approved */
 267 {
 268 int  chkidx;    /* Check index for checksum character */
 269 int  lenidx;    /* Length index for length character */
 270 int  pktl;      /* Packet length */
 271 int  type;      /* Packet type */
 272 int  i;
 273 
 274 
 275 /* : If packet is too short or too long, reject it. */
 276 
 277    pktl = pkt[0] & MASK_ALL;
 278    if (pktl < MinPktLen || pktl > MaxPktLen)
 279    {
 280 #if Debug
 281       if (dbgrejs)
 282       {  if (dbgpkts)
 283             sdbgpkt("RcvPkt/", &pkt[1], pktl);
 284          sdbgmsg("RejPkt/length");
 285       }
 286 #endif
 287       return;
 288    }
 289 
 290 /* : If check-length is incorrect, reject packet */
 291 
 292    lenidx = pktl - EOPLen - ChkLen - LenLen + 1;
 293    if (check_len (pktl + r_ESC_count) != pkt[lenidx])
 294    {
 295 #if Debug
 296       if (dbgrejs)
 297       {  if (!dbgpkts)
 298             sdbgpkt("RcvPkt/", &pkt[1], pktl);
 299          sdbgmsg("RejPkt/chklen");
 300       }
 301 #endif
 302       return;
 303    }
 304 
 305 /* : If check-sum is incorrect, reject packet. */
 306 
 307    chkidx = lenidx + LenLen;
 308    if (chkcrc(&pkt[1], chkidx-1) != pkt[chkidx])
 309    {
 310 #if Debug
 311       if (dbgrejs)
 312       {  if (!dbgpkts)
 313             sdbgpkt("RcvPkt/", &pkt[1], pktl);
 314          sdbgmsg("RejPkt/chksum");
 315       }
 316 #endif
 317       return;
 318    }
 319 
 320 /* : If packet type is valid, dispatch accordingly; else reject packet. */
 321 
 322    type = pkt[SOPLen + 1];
 323    if ((DatOff <= type && type < DatOff+DatCnt) && !rs_pending)
 324       prsdat(pkt);
 325    else if ((AckOff <= type && type < AckOff+AckCnt) && !rs_pending)
 326       prsack(pkt);
 327    else if ((NakOff <= type && type < NakOff+NakCnt) && !rs_pending)
 328       prsnak(pkt);
 329    else if ((BrkOff <= type && type < BrkOff+BrkCnt) && !rs_pending)
 330       prsbrk(pkt);
 331    else if (RstOff <= type && type < RstOff+RstCnt)
 332       prsrst(pkt);
 333    else if (FastDis == type)
 334       disconnect_confirm (True);
 335    else
 336    {
 337 #if Debug
 338       if (dbgrejs)
 339       {  if (!dbgpkts)
 340             sdbgpkt("RcvPkt/", &pkt[1], pktl);
 341          sdbgmsg("RejPkt/type");
 342       }
 343 #endif
 344       return;
 345    }
 346 }
 347 
 348 /*^L*/
 349 
 350 /* : PROCEDURE FUNCTION (check_len)
 351 
 352 Calculate the checklength character based on the length of the entire packet
 353 strip the upper bits, and  add 32 to make it printable.
 354 */
 355 
 356 /* : RETURNS:
 357 
 358 The calculated check length chartacter.
 359 */
 360 
 361 int check_len(length)
 362 
 363 int  length;    /* Length to be converted */
 364 {
 365 
 366    return ((length & MASK_SIX) + MIN_ASCII);
 367 }
 368 
 369 /*^L*/
 370 
 371 /* : PROCEDURE FUNCTION
 372 
 373 Calculate a 6-bit CRC for a character based on a generator polynomial of
 374 
 375            x**6 + x**5 + x**2 + x**0
 376 */
 377 
 378 /* : RETURNS
 379 
 380 A value in range of 0-63 which is the desired CRC.
 381 */
 382 
 383 /* : NOTES
 384 
 385 The result of this function is the remainder produced by synthetic division
 386 modulo 2 of a 7-bit integer (whose bits are the coefficients of the generator
 387 polynomial) into a 14-bit integer (whose top 8 bits are those of the character,
 388 in reverse order, and whose low 6 bits are the low 6 bits of the seed).
 389 
 390 The CRC for a string of characters is calculated by calling 'crc_char' once for
 391 each character inthe block, from first character to last.  The seed for the
 392 first character is the 0 and the seed for each remaining character is the CRC
 393 produced for the previous character.  The CRC produced for the last character
 394 is the CRC for the whole string.
 395 */
 396 
 397 int crc_char (p_chr, p_seed)
 398 
 399 int  p_seed;   /* Value in range of 0-63 which is the seed for the CRC calc */
 400 char p_chr;    /* Char for which CRC is to be calced */
 401 {
 402 int  b;        /* Bit counter */
 403 int  crc;      /* accumulator for CRC */
 404 int  q;        /* next quotient bit of the division */
 405 int  schr;     /* rank of 'chr' shifted right 'b' times */
 406 
 407    crc = p_seed;
 408    schr = p_chr;
 409 
 410 /* : For each of the 8 bits in the character
 411      - q = low bit, if q set then right shift crc
 412      - right shift schr */
 413 
 414    for (b = 0; b < 8; b++)
 415    {  q = (crc + schr) & 1;
 416       crc >>= 1;
 417       if (q)
 418          crc ^= REVPOLY;
 419       schr >>= 1;
 420    }
 421    return (crc);
 422 }
 423 
 424 /*^L*/
 425 
 426 /* : PROCEDURE FUNCTION (chkcrc)
 427 
 428 Calculate the printable ascii value of the CRC for the packet provided.
 429 */
 430 
 431 /* : RETURNS
 432 
 433 The desired CRC.
 434 */
 435 
 436 int chkcrc(str, strl)
 437 
 438 char str[];     /* Address of string */
 439 int  strl;      /* Length of string */
 440 {
 441 int  i;
 442 int  crc;       /* Accumulating CRC */
 443 
 444 /* : CRC the ranks of the characters in the string. */
 445 
 446    crc = INIT_CRC;
 447    for (i = 0; i < strl; i++)
 448    {  crc = crc_char (str[i], crc);
 449    }
 450 
 451 /* : Make it ASCII character between ' ' and '\177'. */
 452 
 453      return (crc + MIN_ASCII);
 454 }
 455 
 456 /*^L*/
 457 
 458 /* : PROCEDURE FUNCTION (disconnect_confirm)
 459 
 460 Set up all necessary flags to indicate that disconnection has been accepted and
 461 send a confirm to the confirm if one is needed.
 462 */
 463 
 464 disconnect_confirm(p_fast)
 465 
 466 int  p_fast;    /* Set to true for a fast (non-confirmed) disconnect */
 467 {
 468 char type;      /* Packet type to send */
 469 
 470 /* : If PC initiated disconnection, then send confirm to confirm */
 471 
 472    if ((ds_pending & InitDis) && (!p_fast))
 473    {  ds_timeout = DISCONNECT_OUT;
 474       ds_pending = 0;
 475       type = DisCon + Confirm;
 476       sndpkt (type,"");
 477       return (0);
 478    }
 479 
 480 /* : Set up the terminating flags accordingly */
 481 
 482    mowse_terminating = True;
 483    ds_pending = 0;
 484    packet_mode = False;
 485    packetize_flag = False;
 486    reset();
 487 }
 488 
 489 /*^L*/
 490 
 491 /* : PROCEDURE FUNCTION (getdat)
 492 
 493 Get any available received data.
 494 */
 495 
 496 /* : RETURNS
 497 
 498 The number of characters stored in the buffer (between 0 and MaxDatLen,
 499 inclusive).
 500 */
 501 
 502 int getdat(chn, data)
 503 
 504 int  chn;       /* Channel */
 505 char *data;     /* Address off buffer to receive data */
 506 {
 507 char *rdat;     /* Data string */
 508 int  i;
 509 int  rdatl;     /* Length of receive data */
 510 
 511 /* : If the received data queue is empty, there is nothing to do. */
 512 
 513    if (r_psn[chn] == s_nasn[chn])
 514       return 0;
 515 
 516 /* : Extract the data in the head element of the queue. */
 517 
 518    rdat = &r_dat[chn][s_nasn[chn]][1];
 519    rdatl = rdat[-1] & MASK_ALL;
 520    for (i = 0; i < rdatl; i++)
 521    {  data[i] = rdat[i];
 522    }
 523    s_nasn[chn] = (s_nasn[chn] + 1) & SeqMsk;
 524 
 525 /* : If the receive window is nearly full, send an Ack to keep it open. */
 526 
 527    if (((s_nasn[chn] - s_lasn[chn]) & SeqMsk) >= RWS-1)
 528       sndack(chn);
 529 
 530    return(rdatl);
 531 }
 532 
 533 /*^L*/
 534 
 535 /* : PROCEDURE FUNCTION (prsack)
 536 
 537 Parse an Ack-packet.  If the packet is valid, flow control information is
 538 updated accordingly.
 539 */
 540 
 541 prsack(pkt)
 542 
 543 char pkt[];     /* The ack-packet to be parsed */
 544 {
 545 int  asn;       /* Ack sequence number */
 546 int  asn_valid; /* Valid Ack sequence number */
 547 int  chn;       /* Channel of ack */
 548 int  fields;    /* Fields of packet */
 549 
 550 /* : If packet length incorrect, reject packet. */
 551 
 552    if (pkt[0] != MinPktLen)
 553    {
 554 #if Debug
 555       if (dbgrejs)
 556       {  if (!dbgpkts)
 557             sdbgpkt("RcvPkt/", &pkt[1], pkt[0] & MASK_ALL);
 558          sdbgmsg("RejAck/length");
 559       }
 560 #endif
 561       return;
 562    }
 563 
 564 /* : Extract the acknowledgement sequence number and channel number. */
 565 
 566    fields = pkt[SOPLen+1] - AckOff;
 567    asn = fields & SeqMsk;
 568    chn = fields >> SeqFld;
 569 
 570 /* : If the acknowledgement sequence number is invalid, reject packet. */
 571 
 572    if (r_asn[chn] <= s_psn[chn])
 573       asn_valid = (r_asn[chn] <= asn && asn <= s_psn[chn]);
 574    else
 575       asn_valid = (r_asn[chn] <= asn || asn <= s_psn[chn]);
 576    if (!asn_valid)
 577    {
 578 #if Debug
 579       if (dbgrejs)
 580       {  if (!dbgpkts)
 581             sdbgpkt("RcvPkt/", &pkt[1], pkt[0] & MASK_ALL);
 582          sdbgmsg("RejAck/asn");
 583       }
 584 #endif
 585       return;
 586    }
 587 
 588 /* : Save the new acknowledgement sequence number. */
 589 
 590    r_asn[chn] = asn;
 591 }
 592 
 593 /*^L*/
 594 
 595 /* : PROCEDURE FUNCTION (prsbrk)
 596 
 597 Parse a Break-packet.
 598 */
 599 
 600 /* : NOTES
 601 
 602 There are two types of break packets - one is the foreground break which serves
 603 to interrupt a foreground activity (not possible on the PC) and the other is
 604 the background break which provides a mechanism for disabling MOWSE on Multics
 605 and to leave the communications open to non-MOWSE Multics
 606 */
 607 
 608 prsbrk (pkt)
 609 
 610 char pkt[];     /* The packet to be parsed */
 611 {
 612 char type;      /* Type field of packet */
 613 
 614 /* : If a Disconnect then send a confirmation to Multics */
 615 
 616    if ((pkt[SOPLen+1] == DisCon + Request) && !ds_pending)
 617    {  ds_pending = ds_pending | RespDis;
 618       pending_timer[Dis_Timer_X] = 0;
 619       type = DisCon + Confirm;
 620       sndpkt (type,"");
 621    }
 622 
 623 /* : If a Disconnect confirmation, then all done and shut off protocol modes */
 624 
 625    else if (pkt[SOPLen + 1] == DisCon + Confirm)
 626    {  pending_timer[Dis_Timer_X] = 0;
 627       disconnect_confirm(0);
 628    }
 629 
 630 /* : If a Foreground break request then handle accordingly */
 631 
 632    else if (pkt[SOPLen + 1] == FGBrk + Request)
 633    {
 634    }
 635 
 636 /* ; If a Foreground break confirm then handle accordingly */
 637    else if (pkt[SOPLen + 1] == FGBrk + Confirm)
 638    {  pending_timer[Break_Timer_X] = 0;
 639       br_pending = False;
 640       type = FGBrk + Confirm;
 641       sndpkt(type,"");
 642    }
 643 }
 644 
 645 /*^L*/
 646 
 647 /* PROCEDURE FUNCTION (prsdat)
 648 
 649 Parse a Data-packet.  If the packet is valid, the data is stored in the receive
 650 data queue.
 651 */
 652 
 653 prsdat(pkt)
 654 
 655 char pkt[];     /* The packet to be parsed */
 656 {
 657 char *data;     /* Accepted data buffer */
 658 char *rdat;     /* Received data */
 659 int  asn;       /* Ack sequence number */
 660 int  asn_valid; /* Accept sequence number valid */
 661 int  chn;       /* Channel */
 662 int  datal;     /* Length of data */
 663 int  fields;    /* Type field */
 664 int  i;
 665 int  psn;       /* Packet sequence number */
 666 
 667 /* : Extract the acknowledgement sequence number, packet sequence number and
 668      channel number from the type char. */
 669 
 670    fields = pkt[SOPLen + 1] - DatOff;
 671    asn = fields & SeqMsk;
 672    psn = (fields >> SeqFld) & SeqMsk;
 673    chn = fields >> (SeqFld + SeqFld);
 674 
 675 /* : If the acknowledgement sequence number is invalid, reject the packet. */
 676 
 677    if (r_asn[chn] <= s_psn[chn])
 678       asn_valid = (r_asn[chn] <= asn && asn <= s_psn[chn]);
 679    else
 680       asn_valid = (r_asn[chn] <= asn || asn <= s_psn[chn]);
 681    if (!asn_valid)
 682    {
 683 #if Debug
 684       if (dbgrejs)
 685       {  if (!dbgpkts)
 686             sdbgpkt("RcvPkt/", &pkt[1], pkt[0] & MASK_ALL);
 687          sdbgmsg("RejDat/asn");
 688       }
 689 #endif
 690       return;
 691    }
 692 
 693 /* : Save the new acknowledgement sequence number. */
 694 
 695    r_asn[chn] = asn;
 696 
 697 /* : If ds_pending then ignore all data */
 698 
 699    if (ds_pending)
 700       return;
 701 
 702 /* : If the send sequence number is not the one we expect, send a Nak-packet,
 703      (unless one has already been sent) and ignore this packet. */
 704 
 705    if (psn != r_psn[chn])
 706    {  if (!r_ignoring[chn])
 707       {  sndnak(chn);
 708          r_ignoring[chn] = True;
 709       }
 710 #if Debug
 711       if (dbgrejs)
 712       {  if (!dbgpkts)
 713             sdbgpkt("RcvPkt/", &pkt[1], pkt[0] & MASK_ALL);
 714          sdbgmsg("RejDat/psn");
 715       }
 716 #endif
 717       return;
 718    }
 719 
 720 /* : Accept the data. */
 721 
 722    data = &pkt[SOPLen + TypLen + 1];
 723    datal = (pkt[0] & MASK_ALL) - MinPktLen;
 724    rdat = &r_dat[chn][psn][1];
 725    rdat[-1] = datal;
 726    for (i = 0; i < datal; i++)
 727    {  rdat[i] = data[i];
 728    }
 729    r_ignoring[chn] = False;
 730    r_psn[chn] = (psn + 1) & SeqMsk;
 731 }
 732 
 733 /*^L*/
 734 
 735 /* : PROCEDURE FUNCTION (prsnak)
 736 
 737 Parse a Nak-packet.  If the packet is valid, any data packets that have been
 738 sent but are not acknowledged by this Nak-packet are resent.
 739 */
 740 
 741 prsnak(pkt)
 742 
 743 char pkt[];     /* The Nak-packet to be parsed */
 744 {
 745 int asn;        /* Ack sequence number */
 746 int asn_valid;  /* Valid ack sequence number */
 747 int chn;        /* Channel */
 748 int fields;     /* Type field of packet */
 749 
 750 /* : If packet length is incorrect, reject the packet. */
 751 
 752    if (pkt[0] != MinPktLen)
 753    {
 754 #if Debug
 755       if (dbgrejs)
 756       {  if (!dbgpkts)
 757             sdbgpkt("RcvPkt/", &pkt[1], pkt[0] & MASK_ALL);
 758          sdbgmsg("RejNak/length");
 759       }
 760 #endif
 761       return;
 762    }
 763 
 764 /* : Extract the acknowledgement sequence number and the channel number from
 765      the type byte. */
 766 
 767    fields = pkt[SOPLen + 1] - NakOff;
 768    asn = fields & SeqMsk;
 769    chn = fields >> SeqFld;
 770 
 771 /* : If the acknowledgement sequence number is invalid, reject the packet. */
 772 
 773    if (r_asn[chn] <= s_psn[chn])
 774       asn_valid = (r_asn[chn] <= asn && asn <= s_psn[chn]);
 775    else
 776       asn_valid = (r_asn[chn] <= asn || asn <= s_psn[chn]);
 777    if (!asn_valid)
 778    {
 779 #if Debug
 780       if (dbgrejs)
 781       {  if (!dbgpkts)
 782             sdbgpkt("RcvPkt/", &pkt[1], pkt[0] & MASK_ALL);
 783          sdbgmsg("RejNak/asn");
 784       }
 785 #endif
 786       return;
 787    }
 788 
 789 /* : Save the new acknowledgement sequence number. */
 790 
 791    r_asn[chn] = asn;
 792 
 793 /* : Resend any data in the send data queue. */
 794 
 795    resend(chn);
 796 }
 797 
 798 /*^L*/
 799 
 800 /* PROCEDURE FUNCTION (prsrst)
 801 
 802 Parse a Reset-packet.  When a reset is complete, set the packetize flag to true
 803 which indicates that protocol is set up completely.
 804 */
 805 
 806 prsrst(pkt)
 807 
 808 char pkt[];     /* The reset packet to be parsed */
 809 {
 810 char type;      /* Type field of a packet */
 811 
 812 /* : If in process of disconnecting then ignore */
 813 
 814    if (ds_pending)
 815       return (0);
 816 
 817 /* : If Multics requested reset: reset and send confirmation and pretend reset
 818      in packet queue */
 819 
 820    if (pkt[SOPLen+1] == RstOff + Request)
 821    {  rs_pending = rs_pending | RespRst;
 822       reset();
 823       r_pktin = 1;
 824       type = RstOff + Confirm;
 825       sndpkt(type, "");
 826    }
 827 
 828 /* : Else if confirmation */
 829 
 830    else if (pkt[SOPLen + 1] == RstOff + Confirm)
 831    {  if (rs_pending & InitRst)
 832       {  pending_timer[Reset_Timer_X] = 0;
 833          type = RstOff + Confirm;
 834          sndpkt (type, "");
 835       }
 836       packetize_flag = True;
 837       rs_pending = 0;
 838    }
 839 }
 840 
 841 /*^L*/
 842 
 843 /* PROCEDURE FUNCTION (rcvchr)
 844 
 845 To receive the next character detected by the interrupt handler for input from
 846 the remote.  If the character is valid, it is added to the packet currently
 847 being assembled.
 848 */
 849 
 850 rcvchr(chr)
 851 
 852 char chr;       /* The character received by the interrupt handler */
 853 {
 854 static int EndPkt = 1;
 855 static int ExtChr = 2;
 856 static int LngPkt = 3;
 857 static int NoRoom = 4;
 858 
 859 int  dbgmsg;    /* Accumulating debug message */
 860 int  nextin;    /* Next packet in */
 861 int  pktl;      /* Packet length */
 862 int  test_ds;   /* Tests if ds_pending is set before processing data */
 863 char *pkt;      /* Accumulating packet */
 864 
 865 /* : If character is a null then ignore it (network created it) */
 866 
 867    if (!chr)
 868       return;
 869 
 870 /* : Replace Null_Convert characters with a NULL character */
 871 
 872    if (chr == Null_Convert)
 873       chr = NULL;
 874 
 875    dbgmsg = 0;
 876 
 877 /* : Assemble packet in next slot of received packet queue. */
 878 
 879    pkt = r_pkt[r_pktin];
 880 
 881 /* : If the received char is the SOP char, start a new packet. */
 882 
 883    if (chr == r_SOP)
 884    {  r_esckey = '\000';
 885       pkt[0] = 1;
 886       pkt[1] = chr;
 887    }
 888 
 889 /* : Else if we are not assembling a packet, discard the received char. */
 890 
 891    else if (pkt[0] == 0)
 892    {  dbgmsg = ExtChr;
 893    }
 894 
 895 /* : Else if the received char is the EOP char, append it to the packet.
 896      If there is room in the received packet queue, make the packet visible (by
 897      updating the "in" pointer); otherwise flush the packet. */
 898 
 899    else if (chr == r_EOP)
 900    {  dbgmsg = EndPkt;
 901       pktl = ++pkt[0] & MASK_ALL;
 902       pkt[pktl] = chr;
 903 
 904       if (r_pktin < RQS)
 905          nextin = r_pktin + 1;
 906       else
 907          nextin = 0;
 908 
 909       if (nextin != r_pktout)
 910       {  r_pktin = nextin;
 911          r_pkt[r_pktin][0] = 0;
 912       }
 913       else
 914       {  dbgmsg = NoRoom;
 915          pkt[0] = 0;
 916       }
 917    }
 918 
 919 /* : Else append the character (or what it stands for if it was preceded
 920      by an escape character) to the buffer, unless it is an escape
 921      character.  If this fills the packet (in which case there will be
 922      no room for the EOP character), flush the packet. */
 923 
 924    else
 925    {  if (r_esckey != '\000')
 926       {  chr = chr ^ r_esckey;
 927          r_esckey = '\000';
 928          r_ESC_count++;
 929       }
 930       else if (chr == r_ESC[0])
 931          r_esckey = '\100';
 932       else if (chr == r_ESC[1])
 933          r_esckey = '\200';
 934       else if (chr == r_ESC[2])
 935          r_esckey = '\300';
 936 
 937       if (r_esckey == '\000')
 938       {  pktl = ++pkt[0] & MASK_ALL;
 939          pkt[pktl] = chr;
 940          if (pktl >= MaxPktLen)
 941          {  dbgmsg = LngPkt;
 942             pkt[0] = 0;
 943          }
 944       }
 945    }
 946 
 947 /* : Print any debugging messages. If printable, send as is, otherwise convert
 948      it to octal, preceed it with \ */
 949 #if Debug
 950    if (dbgmsg != 0)
 951    {  if (dbgmsg == ExtChr)
 952       {  if (dbgxchr)
 953          {  if (chr >= ' ' && chr <= '~')
 954                sdbgchr(chr + REV_VIDEO);
 955             else
 956             {  sdbgchr('\\' + REV_VIDEO);
 957                sdbgchr(((chr>>6) & 3) + '0' + REV_VIDEO);
 958                sdbgchr(((chr>>3) & 7) + '0' + REV_VIDEO);
 959                sdbgchr((chr & 7) + '0' + REV_VIDEO);
 960             }
 961          }
 962       }
 963       else
 964       {  if (dbgpkts)
 965             sdbgpkt("RcvPkt/", &pkt[1], pktl);
 966          if (dbgrejs)
 967          {  if (dbgmsg == LngPkt)
 968             {  if (!dbgpkts)
 969                   sdbgpkt("RcvPkt/", &pkt[1], pktl);
 970                sdbgmsg("RejPkt/LngPkt");
 971             }
 972             else if (dbgmsg == NoRoom)
 973             {  if (!dbgpkts)
 974                   sdbgpkt("RcvPkt/", &pkt[1], pktl);
 975                sdbgmsg("RejPkt/NoRoom");
 976             }
 977          }
 978       }
 979    }
 980 #endif
 981 
 982 /* : If we were approving packets when we were interrupted to receive the
 983      character just processed, we want to simply return so that approving can
 984      continue.  If we were not approving packets and the received packet queue
 985      is not empty, then we want to initiate approving.
 986 */
 987    if (!apv_locked)
 988    {  apv_locked = (r_pktin != r_pktout);
 989       while (apv_locked)
 990       {  test_ds = ds_pending;
 991          apvpkt(r_pkt[r_pktout]);
 992          r_ESC_count = 0;
 993          if (r_pktout < RQS)
 994             r_pktout++;
 995          else
 996             r_pktout = 0;
 997          apv_locked = (r_pktin != r_pktout);
 998 
 999          if ((test_ds & ds_pending) & RespDis)
1000             disconnect_confirm(0);
1001       }
1002    }
1003 }
1004 
1005 /*^L*/
1006 
1007 /* : PROCEDURE FUNCTION (resend)
1008 
1009 Resend all unacknowledged data for a channel.
1010 */
1011 
1012 resend(chn)
1013 
1014 int  chn;       /* Channel number */
1015 {
1016 int  psn;       /* Packet sequence number */
1017 char type;      /* Packet type */
1018 
1019 /* : Resend each packet */
1020 
1021    for (psn = r_asn[chn]; psn != s_psn[chn]; psn = (psn + 1) & SeqMsk)
1022    {  type = (((chn << SeqFld) + psn) << SeqFld) + s_nasn[chn] + DatOff;
1023       sndpkt(type, s_dat[chn][psn]);
1024       s_lasn[chn] = s_nasn[chn];
1025       s_timer[chn] = 0;
1026    }
1027 }
1028 
1029 /*^L*/
1030 
1031 /* PROCEDURE FUNCTION (reset)
1032 
1033 To intialize the flow-control related variables of the protocol.
1034 */
1035 
1036 reset()
1037 
1038 {
1039 int  chn;       /* Channel number */
1040 int  i;
1041 
1042    apv_locked = False;
1043    ds_pending = 0;
1044    br_pending = 0;
1045    for (i = 0; i < 3; pending_timer[i++] = 0);
1046 
1047    for (i = 0; i <= RQS; i++)
1048    {  r_pkt[i][0] = 0;
1049    }
1050    r_pktin = 0;
1051    r_pktout = 0;
1052 
1053    for (chn = 0; chn < ChnCnt; chn++)
1054    {  for (i = 0; i < SeqCnt; i++)
1055       {  r_dat[chn][i][0] = 0;
1056       }
1057       r_ignoring[chn] = False;
1058       r_asn[chn] = 0;
1059       r_psn[chn] = 0;
1060       r_timer[chn] = 0;
1061 
1062       for (i = 0; i < SeqCnt; i++)
1063       {  s_dat[chn][i][0] = 0;
1064       }
1065       s_lasn[chn] = 0;
1066       s_nasn[chn] = 0;
1067       s_psn[chn] = 0;
1068       s_timer[chn] = 0;
1069    }
1070 }
1071 
1072 /*^L*/
1073 
1074 #if Debug
1075 
1076 /* : PROCEDURE FUNCTION (sdbgmsg)
1077 
1078 Send a debugging message.
1079 */
1080 
1081 sdbgmsg(msg)
1082 
1083 char msg[];     /* Message to be shown */
1084 {
1085 int  i;
1086 
1087 /* : Send CR/LF and a reverse video '('. */
1088 
1089    sdbgchr(CR);
1090    sdbgchr(LF);
1091    sdbgchr('(' + REV_VIDEO);
1092 
1093 /* : Send the message. */
1094 
1095    for (i = 0; msg[i] != '\000'; i++)
1096    {  sdbgchr(msg[i]);
1097    }
1098 
1099 /* : Send a reverse-video ')' and CR/LF. */
1100 
1101    sdbgchr(')' + REV_VIDEO);
1102    sdbgchr(CR);
1103    sdbgchr(LF);
1104 }
1105 #endif
1106 
1107 /*^L*/
1108 
1109 #if Debug
1110 
1111 /* : PROCEDURE FUNCTION (sdbgpkt)
1112 
1113 Send a debugging message that displays a packet.
1114 */
1115 
1116 sdbgpkt(msg, pkt, pktl)
1117 
1118 char msg[];     /* Message to be displayed */
1119 char pkt[];     /* Debug packet */
1120 int  pktl;      /* Length of packet */
1121 {
1122 char chr;       /* Temporary char space */
1123 int  i;
1124 
1125 /* : Send CR/LF and a reverse-video '('. */
1126 
1127    sdbgchr(CR);
1128    sdbgchr(LF);
1129    sdbgchr('(' + 128);
1130 
1131 /* : Send the message describing the packet. */
1132 
1133    for (i = 0; msg[i] != '\0'; i++)
1134    {  sdbgchr(msg[i]);
1135    }
1136 
1137 /* : Send the packet with nonprintable characters replaced by an octal
1138      escape sequence. */
1139 
1140    for (i = 0; i < pktl; i++)
1141    {  chr = pkt[i];
1142       if (chr >= ' ' && chr <= '~')
1143          sdbgchr(chr);
1144       else
1145       {  sdbgchr('`');
1146          sdbgchr(((chr>>6) & 3) + '0');
1147          sdbgchr(((chr>>3) & 7) + '0');
1148          sdbgchr((chr & 7) + '0');
1149       }
1150    }
1151 
1152 /* : Send a reverse-video ')' and CR/LF. */
1153 
1154    sdbgchr(')' + 128);
1155    sdbgchr(CR);
1156    sdbgchr(LF);
1157 }
1158 #endif
1159 
1160 /*^L*/
1161 
1162 /* PROCEDURE FUNCTION (sndack)
1163 
1164 Send an Ack-packet.
1165 */
1166 
1167 sndack(chn)
1168 
1169 int  chn;       /* The channel to send the Ack along */
1170 {
1171 char type;      /* Type field */
1172 
1173    type = (chn << SeqFld) + s_nasn[chn] + AckOff;
1174    sndpkt(type, "");
1175    s_lasn[chn] = s_nasn[chn];
1176    r_timer[chn] = 0;
1177 }
1178 
1179 /*^L*/
1180 
1181 /* PROCEDURE FUNCTION (sndbrk)
1182 
1183 Send a Break-packet to signal a break on the foreground channel.
1184 */
1185 
1186 sndbrk()
1187 
1188 {
1189 char type;      /* A packet type field */
1190 
1191 /* : Set up break processing protocol */
1192 
1193    br_pending = True;
1194    pending_timer[Break_Timer_X] = 0;
1195 
1196 /* : Send the break message */
1197 
1198    type = FGBrk + Request;
1199    sndpkt (type, "");
1200 }
1201 
1202 /*^L*/
1203 
1204 /* PROCEDURE FUNCTION (snddat)
1205 
1206 To queue and then send data to the remote.
1207 */
1208 
1209 /* : RETURNS
1210 
1211    -1 => sum of lengths of strings to be sent exceeds 'MaxDatLen'.
1212     0 => data sent.
1213     1 => couldn't send data because send window was full.
1214     2 => couldn't send because reset is pending.
1215 
1216 */
1217 
1218 int snddat(chn, datac, datap, datal)
1219 
1220 int  chn,       /* Channel number */
1221      datac,     /* Count of number of strings to be sent */
1222      datal[];   /* Array of lengths of strings to be sent */
1223 char *datap[];  /* Array of pointers to the strings to be sent */
1224 {
1225 static int TooLong = -1;
1226 static int SentDat = 0;
1227 static int WdwFull = 1;
1228 static int RstPnd  = 2;
1229 
1230 char *sdat;     /* Send data pointer */
1231 char type;      /* Type field */
1232 int  i;
1233 int  j;
1234 int  l;
1235 int  spsn;      /* Send packet sequence number */
1236 
1237 /* : If resetting or not talking yet, then return */
1238 
1239    if ((rs_pending) || (!packetize_flag))
1240       return RstPnd;
1241 
1242 /* : Reject the data if the send data queue is full. */
1243 
1244    spsn = s_psn[chn];
1245    if (((spsn - r_asn[chn]) & SeqMsk) >= SWS)
1246       return WdwFull;
1247 
1248 /* : Catenate the data to be sent in the next slot of the send data queue. */
1249 
1250    sdat = s_dat[chn][spsn];
1251    sdat[0] = 0;
1252    l = 0;
1253    for (i = 0; i < datac; i++)
1254    {  for (j = 0; j < datal[i]; j++)
1255       {  if (l < MaxDatLen)
1256             sdat[++l] = datap[i][j];
1257          else
1258             return TooLong;
1259       }
1260    }
1261    sdat[0] = l;
1262 
1263 /* : Return if there is nothing to send. */
1264 
1265    if (l == 0)
1266       return SentDat;
1267 
1268 /* : Make the catenated data visible by updating the packet sequence number. */
1269 
1270    s_psn[chn] = (spsn + 1) & SeqMsk;
1271 
1272 /* : Send the data and reset resend timer. */
1273 
1274    type = (((chn << SeqFld) + spsn) << SeqFld) + s_nasn[chn] + DatOff;
1275    sndpkt(type, sdat);
1276    s_lasn[chn] = s_nasn[chn];
1277    r_timer[chn] = 0;
1278    s_timer[chn] = 0;
1279    return SentDat;
1280 }
1281 
1282 /*^L*/
1283 
1284 /* : PROCEDURE FUNCTION (snddis)
1285 
1286 Initiate a disconnection from MOWSE on Multics.
1287 */
1288 
1289 snddis()
1290 
1291 {
1292 char type;      /* Type field of message */
1293 
1294 /* : If in not in packet/protocol mode then reject request */
1295 
1296    if (!packetize_flag || !packet_mode)
1297       return (WSNOTACT);
1298 
1299 /* : If a disconnection is currently in progress, ignore it */
1300 
1301    if (ds_pending)
1302       return (WSDISPEN);
1303 
1304 /* : If timeout has occurred, return timeout error */
1305 
1306    if (ds_timeout & TIME_OUT)
1307    {  ds_timeout = 0;
1308       return (TIME_OUT);
1309    }
1310 
1311 /* : If got response, reply that Multics is still active */
1312 
1313    if (ds_timeout & DISCONNECT_OUT)
1314    {  ds_timeout = 0;
1315       return (DISCONNECT_OUT);
1316    }
1317 
1318 /* : Initiate disconnection by sending Disconnect request to Multics */
1319 
1320    ds_pending |= InitDis;
1321    ds_timeout = 0;
1322    type = DisCon + Request;
1323    sndpkt (type, "");
1324    return (WSDISPEN);
1325 }
1326 
1327 /*^L*/
1328 
1329 /* PROCEDURE FUNCTION (sndnak)
1330 
1331 Send a Nak-packet to the remote.
1332 */
1333 
1334 sndnak(chn)
1335 
1336 int  chn;       /* The channel on which to send the Nak-packet */
1337 {
1338 char type;      /* Type field of the packet */
1339 
1340    type = (chn << SeqFld) + /*r_psn*/s_nasn[chn] + NakOff;
1341    sndpkt(type, "");
1342    return (0);
1343 }
1344 
1345 /*^L*/
1346 
1347 /* PROCEDURE FUNCTION (sndpkt)
1348 
1349 Send a packet to the remote.
1350 */
1351 
1352 /* : NOTES
1353 
1354 If MOWSE is not in packet mode state, then packets will not be sent as they
1355 will go to the remote's command processor.
1356 
1357 The packet to be sent is built in the local buffer 'pkt' and then sent to the
1358 remote all at once.  The space required for this buffer could be saved if the
1359 packet were sent to the remote a character at a time.  This was not done
1360 because the time to execute a call to the routine that sends data to the remote
1361 is greater than that to add a character to a buffer, and because to send the
1362 packet in pieces would require that interrupts be inhibited for the duration of
1363 the execution of 'sndpkt' in order to prevent it from being re-entered.
1364 
1365 Data outside the range ' ' through '_' for which the corresponding element of
1366 's_escreq' is set is replaced in the packet via a two character escape sequence
1367 consisting of an element of the array "s_ESC" followed by a printable ASCII
1368 character between ' ' and '_'.  These two characters are chosen such that the
1369 exclusive-or of the second with 64 times the ordinal in 's_ESC' of the first
1370 yields the character they represent.
1371 
1372 The replacement of data characters via two character escape sequences has no
1373 effect upon the checksum of the packet:  The checksum is calculated using the
1374 original data characters rather than those actually sent in the data field of
1375 the packet.  This allows the receiver of the packet to revert escape sequences
1376 as soon as they are encountered.
1377 */
1378 
1379 sndpkt(type, data)
1380 
1381 char type;      /* Type field of message */
1382 char data[];    /* Data of message */
1383 {
1384 static int EscKey[3]    = {1*64, 2*64, 3*64},
1385            GrpEscIdx[8] = {0, -1, -1, 0, 2, 1, 1, 2};
1386 
1387 int  crc;                              /* CRC character */
1388 int  chr;                              /* Character */
1389 int  datal;                            /* Length of data */
1390 int  escidx;                           /* Escape index */
1391 int  i;
1392 int  group;                            /* Group number of escape character */
1393 int  pktl;                             /* Packet length */
1394 char pkt[MinPktLen + 2*MaxDatLen];     /* Accumulating send packet */
1395 
1396 /* : If not in packet mode (remote not MOWSE active), return */
1397 
1398    if (!packet_mode)
1399       return (0);
1400 
1401 /* : If a disconnect is in progress and the type is not one of the disconnect
1402      packets, return */
1403 
1404    if (ds_pending && ((type != DisCon + Request) && (type != DisCon + Confirm)))
1405       return(0);
1406 
1407 /* : Assemble the packet.  Insert escape sequences as required. */
1408 
1409    pkt[0] = s_SOP;
1410    pkt[1] = type;
1411    crc = crc_char (pkt[0], INIT_CRC);
1412    crc = crc_char (pkt[1], crc);
1413    pktl = 2;
1414    datal = data[0] & MASK_ALL;
1415    for (i = 1; i <= datal; i++)
1416    {  chr = data[i] & MASK_ALL;
1417       crc = crc_char ((char)chr, crc);
1418       group = chr >> 5;                /* Divide char by 32 to get group number */
1419       escidx = GrpEscIdx[group];
1420       if (escidx >= 0 && s_escreq[chr])
1421       {  pkt[pktl] = s_ESC[escidx];
1422          pktl++;
1423          chr ^= EscKey[escidx];
1424       }
1425       pkt[pktl] = chr;
1426       pktl++;
1427    }
1428 
1429 /* : Insert the control information */
1430 
1431    pkt[pktl] = check_len (pktl + LenLen + ChkLen + SOPLen);
1432    crc = crc_char (pkt[pktl], crc);
1433    pktl++;
1434    pkt[pktl] = crc + MIN_ASCII;
1435    pktl++;
1436    pkt[pktl] = s_EOP;
1437    pktl++;
1438 
1439 /* : Send the packet to the remote. */
1440 
1441    smdmstr(&pkt[0], pktl);
1442 
1443 #if Debug
1444    if (dbgpkts)
1445    {  sdbgpkt("SndPkt/", &pkt[0], pktl);
1446    }
1447 #endif
1448 }
1449 
1450 /*^L*/
1451 
1452 /* : PROCEDURE FUNCTION (sndrst)
1453 
1454 Send a Reset-packet.
1455 */
1456 
1457 sndrst()
1458 
1459 {
1460 char type;      /* Packet type field */
1461 
1462 /* : If currently resetting, ignore */
1463 
1464    if (rs_pending)
1465       return (0);
1466 
1467 /* : Send reset */
1468 
1469    rs_pending = rs_pending | InitRst;
1470    reset();
1471    type = RstOff + Request;
1472    sndpkt(type, "");
1473 }