home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / tandem.zip / tandem.tal < prev    next >
Text File  |  1988-08-16  |  40KB  |  1,360 lines

  1. ?NOCODE
  2. ?INSPECT
  3. DEFINE VERSTRING = "Tandem KERMIT server - Version 1.0"#;
  4. !*****************************************************************************!
  5. !*                                                                           *!
  6. !*                     TANDEM KERMIT SERVER                                  *!
  7. !*                     VERSION 1.0                                           *!
  8. !*                     MARCH 6, 1986                                         *!
  9. !*                                                                           *!
  10. !*      PROGRAMMER: CHARLES J. CANTOR                                        *!
  11. !*                  CANTOR CONSULTING                                        *!
  12. !*                  116 DICKERMAN RD.                                        *!
  13. !*                  NEWTON, MA 02161                                         *!
  14. !*                                                                           *!
  15. !*      Revision history (newest to oldest):                                 *!
  16. !*      VERSION  DATE      BY/DESCRIPTION                                    *!
  17. !*        1.0    03/06/86  A. G. Camas (Bedford, MA) - Added "[NO]BREAK"     *!
  18. !*                         option.  Added handling for "I" (initialize)      *!
  19. !*                         packet now used before server is asked to send.   *!
  20. !*                         Made fancier "banner" with version number.        *!
  21. !*                                                                           *!
  22. !*        0.0    11/01/84  Charles J. Cantor (Cantor Consulting, Newton, MA) *!
  23. !*                         - Original version of software.                   *!
  24. !*                                                                           *!
  25. !*                                                                           *!
  26. !*      This server will send and receive ASCII files only.                  *!
  27. !*      The output file will be an EDIT file.                                *!
  28. !*                                                                           *!
  29. !*      THE FOLLOWING HAVE NOT BEEN IMPLEMENTED:                             *!
  30. !*      Generic commands other than LOGOFF (Hangs up Modem).                 *!
  31. !*      Host commands.                                                       *!
  32. !*      Wild carding.                                                        *!
  33. !*                                                                           *!
  34. !*      Repeat counts have only been unit tested.                            *!
  35. !*                                                                           *!
  36. !*      Eight bit quoting is implemented in the send and receive procs.      *!
  37. !*      The EDIT file implementation appends <cr><lf> to output              *!
  38. !*      lines and strips them on input, rendering the issue moot;            *!
  39. !*      therefore, it has been defaulted out and also butchered out in       *!
  40. !*      PROC PROCESS^SEND^INIT                                               *!
  41. !*                                                                           *!
  42. !*      COMPILATION:                                                         *!
  43. !*      TAL /IN KERMITS,OUT $S.#KERM/KERMIT                                  *!
  44. !*                                                                           *!
  45. !*      USAGE:                                                               *!
  46. !*                                                                           *!
  47. !*      RUN KERMIT <option-list>                                             *!
  48. !*                                                                           *!
  49. !*      Where <option-list> consists of a series of options separated        *!
  50. !*      by commas.                                                           *!
  51. !*                                                                           *!
  52. !*          OPTION:              DEFAULT:                                    *!
  53. !*                                                                           *!
  54. !*          [NO]DEBUG            NODEBUG                                     *!
  55. !*          [NO]TABS             TABS                                        *!
  56. !*          [NO]TRUNC            NOTRUNC                                     *!
  57. !*          [NO]FLIP             NOFLIP                                      *!
  58. !*          [NO]PURGE            NOPURGE                                     *!
  59. !*          [NO]BREAK            BREAK                                       *!
  60. !*                                                                           *!
  61. !*      Specifying DEBUG allows non-protocol interaction at a                *!
  62. !*      terminal, i. e. no <soh>'s are sent or expected and no               *!
  63. !*      checksumming of input is done.                                       *!
  64. !*                                                                           *!
  65. !*      Unless NOTABS is specified, tabs will be expanded to spaces          *!
  66. !*      on input. Tab stops are the usual every 8'th column. No tab          *!
  67. !*      expansion is done on output.                                         *!
  68. !*                                                                           *!
  69. !*      TRUNC will truncate file-specs at the first decimal point on         *!
  70. !*      send requests, e. g. SEND KERMIT.A86 will go to TANDEM file          *!
  71. !*      KERMIT.                                                              *!
  72. !*                                                                           *!
  73. !*      FLIP will flip file.ext in send requests.                            *!
  74. !*      SEND KERMIT.A86 will go to TANDEM file  <vol>.A86.KERMIT.            *!
  75. !*                                                                           *!
  76. !*      PURGE will purge existing files on send requests.                    *!
  77. !*                                                                           *!
  78. !*      If you specify NOBREAK, interruption by the BREAK key is             *!
  79. !*      disabled.  On some noisy lines, this can help since line             *!
  80. !*      noise is sometimes seen as BREAK, which interrupts the server.       *!
  81. !*      NOBREAK should not be a problem, since ctrl/Y stops KERMIT.          *!
  82. !*                                                                           *!
  83. !*      THE SERVER WILL ACCEPT AND PARSE FILE SPECS GENERATED BY:            *!
  84. !*                                                                           *!
  85. !*      SEND local-file-spec[~tandem-file-spec]                              *!
  86. !*      GET tandem-file-spec[~local-file-spec]                               *!
  87. !*                                                                           *!
  88. !*      The SERVER will return the local-file-spec on a receive              *!
  89. !*      (default is tandem-file-spec), e.g.                                  *!
  90. !*      KERMIT-86>GET DOCUMENT.KERPROTO~KERPROTO.DOC                         *!
  91. !*                                                                           *!
  92. !*      Changes would have to be made to local KERMITs to implement          *!
  93. !*      the augmentation of the SEND command.                                *!
  94. !*                                                                           *!
  95. !*      IDIOSYNCRACIES:                                                      *!
  96. !*      Trailing blanks are trimmed on input.                                *!
  97. !*      It is a good idea to run the server from a command file:             *!
  98. !*          :RUN KERMIT                                                      *!
  99. !*          :INITTERM                                                        *!
  100. !*      as the SETMODES leave the terminal in a strange state if             *!
  101. !*      it aborts.                                                           *!
  102. !*                                                                           *!
  103. !*      RUNNING ON LINES GENNED FOR 6530's WITHOUT 6530 EMULATION:           *!
  104. !*      The TANDEM generates "go into conversational mode" sequences         *!
  105. !*      (<soh>C<etx><LRC>) from time to time. ACK (<ctrl-F>) them.           *!
  106. !*                                                                           *!
  107. !*      It also generates an <enq> (<ctrl-E>) from time to time when         *!
  108. !*      sending large amounts of data to the terminal. Any character         *!
  109. !*      takes care of them; however, an ACK is expected and will not be      *!
  110. !*      echoed. The TANDEM will eventually time out and continue output      *!
  111. !*      in any case.                                                         *!
  112. !*                                                                           *!
  113. !*      Avoid block mode (e.g. XVS) like the plague.                         *!
  114. !*                                                                           *!
  115. !*      EXITING THE SERVER:                                                  *!
  116. !*      <ctry-y> at terminal emulation level exits the server.               *!
  117. !*                                                                           *!
  118. !*****************************************************************************!
  119.  
  120. ?PAGE "GLOBALS"
  121. ?NOLIST,SOURCE $SYSTEM.SYSTEM.GPLDEFS
  122. ?LIST
  123. ?PAGE
  124. LITERAL SOH = 1,
  125.         ETX = 3,
  126.         TAB = %11,
  127.         CR  = %15,
  128.         LF  = %12,
  129.         CTRL^Y = %31,
  130.         TRUE = -1,
  131.         FALSE = 0;
  132.  
  133. DEFINE  LEGAL^PACKETS^D(INIT) =
  134.             STRING  LEGAL^PACKETS ='P' := [INIT,0];
  135.             STRING  .POINTER;
  136.             INT N#,
  137.  
  138.        CHECK^LEGAL^D =
  139.             SCAN LEGAL^PACKETS UNTIL PACKET^TYPE -> @POINTER;
  140.             N := @POINTER '-' @LEGAL^PACKETS#,
  141.  
  142.         BAD^PACKET^D =
  143.             BEGIN
  144.             PACKET^TYPE := -1;
  145.             RETURN;
  146.             END#,
  147.  
  148.         CLOSE^FILE^D(FCB) =
  149.             BEGIN
  150.             IF FILE^OPEN^FLAG THEN
  151.                 BEGIN
  152.                 CALL CLOSE^FILE(FCB);
  153.                 FILE^OPEN^FLAG := FALSE;
  154.                 END;
  155.             END#,
  156.  
  157.         ERROR^MESSAGE^D(A) =
  158.             BEGIN
  159.             IN^BUF^S ':=' A & 0;
  160.             CALL ERROR^PACKET(,IN^BUF^S);
  161.             END#,
  162.  
  163.         NOT^IMPL^D = ERROR^MESSAGE^D("Command not implemented")#,
  164.  
  165.         NO^PROC(A) =
  166.             PROC A;
  167.             BEGIN
  168.             END#,
  169.  
  170.         I^NO^PROC(A) =
  171.             INT PROC A;
  172.             BEGIN
  173.             RETURN TRUE;
  174.             END#;
  175.  
  176. STRUCT PARAMS(*);
  177.     BEGIN
  178.     STRING BUFSIZ;
  179.     STRING TIMOUT;
  180.     STRING NPAD;
  181.     STRING PAD^CHAR;
  182.     STRING EOL;
  183.     STRING QUOTE^CHAR;
  184.     STRING EIGHT^BIT;
  185.     STRING CHECK^TYPE;
  186.     STRING REPEAT^CHAR;
  187.     STRING RESERVED[0:1];
  188.     END;
  189.  
  190. DEFINE CHAR(X) = ((X) + " ")#,
  191.        UNCHAR(X) = ((X) - " ")#,
  192.        CTL(X) = ((X) XOR %100)#,
  193.        CHKSUM(X) = (((X) + ((X) '>>' 6)) LAND %77)#,
  194.        SIX^BIT(X) = ((X) LAND %77)#,
  195.        MARK =  SOH#;
  196.  
  197. STRING .IN^BUF^S[0:511],  !LINE BUFFER!
  198.        .FILE^BUF^S[0:4095];  !FILE BLOCK BUFFER!
  199.  
  200. STRING .OUT^PACKET[0:99],SAVE^SUM;
  201.  
  202. INT OUT^PACKET^LENGTH,IN^PACKET^LENGTH;
  203.  
  204. INT .IN^BUF := @IN^BUF^S '>>' 1,
  205.     .FILE^BUF := @FILE^BUF^S '>>' 1;
  206.  
  207. INT DEBUG^FLAG := FALSE, REPEAT^FLAG, MAX^DATA^CHARS, TABS^FLAG := TRUE;
  208. INT FLIP^FLAG := FALSE, INIT^FAILED := FALSE, TRUNC^FLAG := FALSE;
  209. INT PURGE^FLAG := FALSE;
  210. INT BREAK^FLAG := TRUE;
  211. INT FILE^OPEN^FLAG := FALSE;
  212.  
  213. INT .OLD^BREAK[0:1];   !SAVES PREVIOUS BREAK DATA IF NOBREAK MODE!
  214.  
  215. STRING .SBUF[0:511];   !SCRATCH BUFFER AND FILE I/O BUFFER!
  216. INT .BUF := @SBUF '>>' 1;
  217. LITERAL OUT^BLKLEN = 4096;
  218.  
  219. ALLOCATE^CBS(RUCB,COMMON^FCB,3);
  220. ALLOCATE^FCB(IN^FCB,"        #IN             ");
  221. ALLOCATE^FCB(FILE^FCB,"                JUNK    ");   !PICK UP DEFAULT!
  222. ALLOCATE^FCB(ERR^FCB,"        #TERM           ");
  223. INT .DEFAULT^VOL[0:7];
  224. INT .FILE^NAME;
  225. INT .TERM^NAME;
  226. INT .ERR^NAME;
  227. INT TERMNUM;
  228. INT .FILENUM;
  229.  
  230. STRING PACKET^TYPE;
  231.  
  232. STRING .DEFAUL[0:$LEN(PARAMS) - 1] := [
  233.                                        CHAR(94), !MAX BUFSIZE!
  234.                                        CHAR(5),  !TIME OUT!
  235.                                        CHAR(0),  !NUMBER OF PAD CHARS!
  236.                                        CTL(0),   !PAD CHARACTER!
  237.                                        CHAR(CR), !END OF LINE CHARACTER!
  238.                                        "#",      !CONTROL QUOTE!
  239.                                        "N",      !8 BIT QUOTE!
  240.                                        "1",      !CHKSUM TYPE!
  241.                                        "~",      !REPEAT QUOTER!
  242.                                        "  "];    !RESERVED!
  243. STRING .MY^PARAM^STRING[0:$LEN(PARAMS) - 1];
  244. STRING .HIS^PARAM^STRING[0:$LEN(PARAMS) - 1];
  245. STRING .MY^PARAMS(PARAMS) := @MY^PARAM^STRING;
  246. STRING .HIS^PARAMS(PARAMS) := @HIS^PARAM^STRING;
  247.  
  248. INT WAIT^FOREVER,
  249.     MAX^RETRIES := 5,
  250.     NUM^RETRIES := 0;
  251.  
  252. INT(32) TIME^OUT;
  253. STRING  PACKET^NUMBER := 0,
  254.         INPUT^PACKET^NUMBER := 0;
  255.  
  256. STRUCT START^MSG^DEF(*);
  257.     BEGIN
  258.     INT  MSGCODE;
  259.     STRUCT DEFAULT;
  260.          BEGIN
  261.          INT VOLUME[0:3];
  262.          INT SUBVOL[0:3];
  263.          END;
  264.     STRUCT INFILE;
  265.          BEGIN
  266.          INT VOLUME[0:3];
  267.          INT SUBVOL[0:3];
  268.          INT DNAME[0:3];
  269.          END;
  270.     STRUCT OUTFILE;
  271.          BEGIN
  272.          INT VOLUME[0:3];
  273.          INT SUBVOL[0:3];
  274.          INT DNAME[0:3];
  275.          END;
  276.     STRING PARAM[0:596];
  277.     END;
  278. ?NOLIST,SOURCE $SYSTEM.SYSTEM.EXTDECS
  279. ?LIST
  280.  
  281. ?PAGE "FORWARDS"
  282. PROC GET^PACKET(RETRIES) VARIABLE;
  283. INT RETRIES;
  284. FORWARD;
  285. PROC ERROR^PACKET(ERROR,MESSAGE) VARIABLE;
  286. INT ERROR;
  287. STRING .MESSAGE;
  288. FORWARD;
  289. ?PAGE "START^PROC"
  290. PROC START^PROC (RUCB,PASSTHRU,MESSAGE,MESLEN,MATCH) VARIABLE;
  291.  
  292. ! THIS PROC PARSES THE PARAMS PORTION OF THE START UP MESSAGE!
  293.  
  294. INT .RUCB,.PASSTHRU,MESLEN,MATCH;
  295. STRUCT .MESSAGE(START^MSG^DEF);
  296. BEGIN
  297. INT DONE := FALSE,MATCHED;
  298. STRING .MES^S,.TEMP;
  299.  
  300. DEFINE MATCH^IT(A,FLAG) =
  301. BEGIN
  302. IF MES^S = "NO" AND MES^S[2] = A THEN
  303.            BEGIN
  304.            MATCHED := TRUE;
  305.            FLAG := FALSE;
  306.            END;
  307. IF MES^S = A THEN
  308.            BEGIN
  309.            MATCHED := TRUE;
  310.            FLAG := TRUE;
  311.            END;
  312. END#;
  313.  
  314. @MES^S := @MESSAGE '<<' 1;
  315.  
  316. MES^S[MESLEN] := 0;
  317. @MES^S := @MES^S[$OFFSET(MESSAGE.PARAM)];
  318. CALL SHIFTSTRING(MES^S,MESLEN - $OFFSET(MESSAGE.PARAM),0);   !UPPER CASE!
  319.  
  320. WHILE NOT DONE DO
  321.     BEGIN
  322.     SCAN MES^S WHILE " " -> @MES^S; !STRIP LEADING BLANKS!
  323.     SCAN MES^S UNTIL "," -> @TEMP;
  324.     IF $CARRY THEN
  325.         BEGIN
  326.         DONE := TRUE;
  327.         IF @MES^S = @TEMP THEN RETURN;
  328.         MATCHED := FALSE;
  329.         END;
  330.  
  331.     MATCH^IT("TABS",TABS^FLAG);
  332.     MATCH^IT("DEBUG",DEBUG^FLAG);
  333.     MATCH^IT("PURGE",PURGE^FLAG);
  334.     MATCH^IT("TRUNC",TRUNC^FLAG);
  335.     MATCH^IT("FLIP",FLIP^FLAG);
  336.     MATCH^IT("BREAK",BREAK^FLAG);
  337.     IF NOT MATCHED THEN
  338.         BEGIN
  339.         INIT^FAILED := TRUE;
  340.         DONE := TRUE;
  341.         END;
  342.     @MES^S := @TEMP[1];
  343.   END;
  344. RETURN;
  345.  
  346. END;
  347. ?PAGE "MISCELLANEOUS SERVICE PROCS"
  348. INT PROC CHECKSUMMER(POINTER,LENGTH);
  349.  
  350. ! THIS PROC COMPUTES THE CHECKSUMS FOR BOTH INBOUND AND OUT BOUND PACKETS!
  351. ! IT RETURNS THE LENGTH OF THE PACKET !
  352. ! LENGTH ON INPUT IS THE ACTUAL LENGTH: SOH -> CHECKSUM!
  353. STRING .POINTER;
  354. INT LENGTH;
  355. BEGIN
  356. INT N,TEMP,TEMP1 := 0;
  357.  
  358. SAVE^SUM := POINTER[LENGTH - 1];
  359. TEMP := 0;
  360.  
  361. FOR N := 1 TO LENGTH - 2 DO
  362.     BEGIN
  363.     TEMP1.<8:15> := POINTER[N].<8:15>;
  364.     TEMP := TEMP + TEMP1;
  365.     END;
  366.  
  367. POINTER[LENGTH - 1] := CHAR(CHKSUM(TEMP.<8:15>));
  368.  
  369. POINTER[LENGTH] := UNCHAR(HIS^PARAMS.EOL);
  370. IF POINTER[LENGTH] = 0 THEN RETURN LENGTH
  371.                        ELSE RETURN LENGTH + 1;
  372. END;
  373.  
  374.  
  375.  
  376.  
  377. PROC FINISH^AND^SHIP(LENGTH,RETRIES) VARIABLE;
  378.  
  379. ! THIS PROC WILL COMPLETE THE SENDING OF A GENERAL DATA PACKET AND RETRIEVE
  380. ! THE REPLY. THE PACKET IS EXPECTED IN OUT^PACKET.
  381. ! LENGTH IS THE LENGTH OF THE DATA IN THE PACKET, I.E. THE CHARACTERS AFTER
  382. ! PACKET TYPE BYTE.
  383.  
  384. INT LENGTH,RETRIES;
  385. BEGIN
  386. IF NOT $PARAM(LENGTH) THEN LENGTH := 0;
  387. IF NOT $PARAM(RETRIES) THEN RETRIES := MAX^RETRIES;
  388. LENGTH := LENGTH + 3;
  389.  
  390. OUT^PACKET[1] := CHAR(LENGTH);
  391. LENGTH := LENGTH + 2;
  392.  
  393. PACKET^NUMBER := SIX^BIT(INPUT^PACKET^NUMBER + 1);
  394. OUT^PACKET[2] := CHAR(PACKET^NUMBER);
  395.  
  396. OUT^PACKET^LENGTH := CHECKSUMMER(OUT^PACKET,LENGTH);
  397. CALL GET^PACKET;
  398. RETURN;
  399. END;
  400. ?PAGE
  401. PROC ERROR^PACKET(ERROR,MESSAGE) VARIABLE;
  402.  
  403. ! THIS PROC SENDS AN ERROR PACKET. IF IT IS CALLED WITH AN ERROR PARAMETER,
  404. ! IT ASSUMES A FILE ERROR AND FORMATS AN APPROPRIATE MESSAGE
  405. ! IF ITS IS CALLED WITH A MESSAGE, IT ASSUMES THAT SHOULD BE SENT
  406.  
  407. INT ERROR;
  408. STRING .MESSAGE;
  409. BEGIN
  410. INT LENGTH;
  411. STRING .FILE^ERROR[0:19] := "  FILE ERROR:       ";
  412.  
  413. OUT^PACKET ':=' [MARK,"LNE"];
  414. LENGTH := 0;
  415. IF $PARAM(ERROR) THEN
  416.     BEGIN
  417.     CALL NUMOUT(FILE^ERROR[14],ERROR,10,3);
  418.     OUT^PACKET[4] ':=' FILE^ERROR FOR 20;
  419.     LENGTH := 20;
  420.     END;
  421.  
  422. IF $PARAM(MESSAGE) THEN
  423.     BEGIN
  424.     SCAN MESSAGE UNTIL 0 -> LENGTH;
  425.     LENGTH := LENGTH '-' @MESSAGE;
  426.     OUT^PACKET[4] ':=' MESSAGE FOR LENGTH;
  427.     END;
  428.  
  429. INPUT^PACKET^NUMBER := 77;
  430. CALL FINISH^AND^SHIP(LENGTH,2);
  431. PACKET^TYPE := -1;
  432. END;
  433.  
  434.  
  435.  
  436.  
  437.  
  438. INT PROC OPEN^INPUT^FILE;
  439. BEGIN
  440. INT ERROR;
  441. CALL SET^FILE(FILE^FCB,ASSIGN^OPENACCESS, READ^ACCESS);
  442. ERROR := OPEN^FILE(COMMON^FCB,FILE^FCB,FILE^BUF,OUT^BLKLEN,
  443.          ,ABORT^OPENERR+ABORT^XFERERR+AUTO^CREATE);
  444. IF ERROR = 0 THEN
  445.     BEGIN
  446.     FILE^OPEN^FLAG := TRUE;
  447.     RETURN TRUE;
  448.     END;
  449. CALL ERROR^PACKET(ERROR);
  450. RETURN FALSE;
  451. END;
  452.  
  453.  
  454.  
  455. INT PROC OPEN^OUTPUT^FILE;
  456. BEGIN
  457. INT ERROR;
  458. INT(32) FLAGS := AUTO^CREATE;
  459.  
  460. IF PURGE^FLAG THEN FLAGS := FLAGS + PURGE^DATA
  461.               ELSE FLAGS := FLAGS + MUSTBENEW;
  462.  
  463. CALL SET^FILE(FILE^FCB,ASSIGN^OPENACCESS, WRITE^ACCESS);
  464. ERROR := OPEN^FILE(COMMON^FCB,FILE^FCB,FILE^BUF,OUT^BLKLEN,
  465.          FLAGS,
  466.          ABORT^OPENERR+ABORT^XFERERR+AUTO^CREATE+PURGE^DATA+MUSTBENEW);
  467. IF ERROR = 0 THEN
  468.     BEGIN
  469.     FILE^OPEN^FLAG := TRUE;
  470.     RETURN TRUE;
  471.     END;
  472. CALL ERROR^PACKET(ERROR);
  473. RETURN FALSE;
  474. END;
  475.  
  476.  
  477.  
  478.  
  479. INT PROC WRITE^OUTPUT^FILE(LENGTH);
  480. INT LENGTH;
  481. BEGIN
  482. INT ERROR;
  483. ERROR := WRITE^FILE(FILE^FCB,BUF,LENGTH);
  484. IF ERROR = 0 THEN RETURN TRUE;
  485. CALL ERROR^PACKET(ERROR);
  486. RETURN FALSE;
  487. END;
  488.  
  489.  
  490.  
  491.  
  492. PROC GIVE^IT^UP(HANG^UP) VARIABLE;
  493.  
  494. INT HANG^UP;
  495.  
  496. ! THIS IS THE CLEAN UP AND EXIT PROC.
  497. ! IT IS CALLED WHEN THE MAX RETRY COUNT IS EXCEEDED OR AN UNRECOVERABLE
  498. ! ERROR OCCURS ON THE LINE
  499. ! IF THE HANG UP IS INCLUDED, IT ALSO HANGS UP THE MODEM
  500.  
  501. BEGIN
  502. CLOSE^FILE^D(FILE^FCB);
  503. CALL CLOSE(TERMNUM);
  504. CALL OPEN(TERM^NAME,TERMNUM);  !REOPEN  WAITED!
  505. CALL SETMODE(TERMNUM,28,0);
  506. IF NOT BREAK^FLAG THEN CALL SETMODE(TERMNUM,11,OLD^BREAK[0],
  507.                                     OLD^BREAK[1]);  !RESET BREAK TO CI!
  508. IF $PARAM(HANG^UP) THEN CALL CONTROL(TERMNUM,12);   !HANG UP!
  509. CALL STOP;
  510. END;
  511.  
  512. ?PAGE "PROC INITIALIZE"
  513. PROC INITIALIZE;
  514. BEGIN
  515. INT FLAG := 0,ERROR;
  516.  
  517. SBUF ':=' [5,"INPUT"];
  518. CALL SET^FILE(IN^FCB,ASSIGN^LOGICALFILENAME,@BUF);
  519. SBUF ':=' [6,"OUTPUT"];
  520. CALL SET^FILE(FILE^FCB,ASSIGN^LOGICALFILENAME,@BUF);
  521. SBUF ':=' [5,"ERROR"];
  522. CALL SET^FILE(ERR^FCB,ASSIGN^LOGICALFILENAME,@BUF);
  523. CALL INITIALIZER(RUCB,,START^PROC);
  524.  
  525. @TERM^NAME := CHECK^FILE( IN^FCB, FILE^FILENAME^ADDR);
  526. @FILE^NAME := CHECK^FILE(FILE^FCB, FILE^FILENAME^ADDR);
  527. @ERR^NAME := CHECK^FILE(ERR^FCB, FILE^FILENAME^ADDR);
  528. DEFAULT^VOL ':=' FILE^NAME FOR 8;
  529.  
  530.  
  531. FLAG.<3:5> := 0;        !READ WRITE!
  532. FLAG.<8> := 1;          !NOWAIT!
  533. FLAG.<12:15> := 7;      !MAX-CONCURRENT NOWAIT IO!
  534.  
  535.  
  536. CALL SET^FILE(ERR^FCB,ASSIGN^OPENACCESS, WRITE^ACCESS);
  537. CALL OPEN^FILE(COMMON^FCB,ERR^FCB,,,
  538.         PURGE^DATA !FLAGS!,PURGE^DATA !MASK!,!LEN!,!PROMPT!,ERR^FCB);
  539. CALL OPEN(TERM^NAME,TERMNUM);     !OPEN WAITED TO DO THE SETMODES!
  540. IF <> THEN CALL DEBUG;
  541.  
  542. IF INIT^FAILED THEN
  543.     BEGIN
  544.     SBUF ':=' "PARAMETER ERROR, KERMIT ABORTED" -> ERROR;
  545.     CALL WRITE(TERMNUM,BUF,ERROR - @SBUF);
  546.     CALL STOP;
  547.     END;
  548. SBUF ':=' VERSTRING -> ERROR;
  549. CALL WRITE(TERMNUM,BUF,ERROR - @SBUF);
  550. SBUF ':=' "Use CTRL/Y to exit" -> ERROR;
  551. CALL WRITE(TERMNUM,BUF,ERROR - @SBUF);
  552.  
  553. MY^PARAMS ':=' DEFAUL FOR $LEN(PARAMS);
  554. HIS^PARAMS ':=' MY^PARAMS FOR $LEN(PARAMS);
  555. MY^PARAMS.TIMOUT := CHAR(0);
  556.  
  557. ERROR := 0;
  558. ERROR.<8:15> := UNCHAR(HIS^PARAMS.TIMOUT);
  559. TIME^OUT := $DBLL(0,100*ERROR);
  560.  
  561. IF DEBUG^FLAG THEN RETURN;
  562.  
  563. SBUF ':=' [CTRL^Y,CTRL^Y,CTRL^Y,CR];   !EOF & CR ONLY INTERRUPT CHARACTERS!
  564. CALL SETMODE(TERMNUM,9,BUF,BUF[1]);   !SET LINE TERMINATION!
  565. CALL SETMODE(TERMNUM,10,0);     !NO-PARITY CHECKING!
  566. CALL SETMODE(TERMNUM,6,0);      !NO SPACE!
  567. CALL SETMODE(TERMNUM,7,0);      !NO LINE FEED AFTER CR!
  568. CALL SETMODE(TERMNUM,20,0);     !NO-ECHO!
  569. !If NOBREAK mode, disable break and save old break parameters
  570. IF NOT BREAK^FLAG THEN CALL SETMODE(TERMNUM,11,0,0,OLD^BREAK);
  571.  
  572. CALL CLOSE(TERMNUM);                 !CLOSE AND
  573. CALL OPEN(TERM^NAME,TERMNUM,FLAG);   !RE-OPEN NO WAIT!
  574. IF <> THEN CALL DEBUG;
  575.  
  576. RETURN;
  577. END;
  578. ?PAGE "ACK AND NAK FORMATTING PROCS"
  579. PROC FORMAT^ACK;
  580. BEGIN
  581. PACKET^NUMBER := INPUT^PACKET^NUMBER;
  582. OUT^PACKET ':=' [MARK,CHAR(3),0,"Y",0];
  583. OUT^PACKET[2] := CHAR(PACKET^NUMBER);
  584. OUT^PACKET^LENGTH := CHECKSUMMER(OUT^PACKET,5);
  585. RETURN;
  586. END;
  587.  
  588.  
  589.  
  590.  
  591.  
  592. PROC FORMAT^NAK;
  593. BEGIN
  594. OUT^PACKET ':=' [MARK,CHAR(3),0,"N",0];
  595. OUT^PACKET[2] := CHAR(PACKET^NUMBER);
  596. OUT^PACKET^LENGTH := CHECKSUMMER(OUT^PACKET,5);
  597. RETURN;
  598. END;
  599.  
  600.  
  601.  
  602. ?PAGE  "PROC GET^PACKET"
  603. PROC GET^PACKET(RETRIES) VARIABLE;
  604.  
  605. ! THIS IS THE LOW LEVEL LINE MANAGEMENT PROTOCOL PROC !
  606.  
  607.  
  608. INT RETRIES;
  609. BEGIN
  610. STRING NON^FATALS = 'P' := [40,120,140,0];
  611. INT COUNT,ERROR,MODEM^TRIES := 0;
  612.  
  613.  
  614.  
  615.  
  616.  
  617.  
  618. INT SUBPROC PAD^PACKET;
  619.  
  620. ! THIS PADS AND MOVES THE OUTPUT PACKET FORM OUT^PACKET TO THE
  621. ! LINE BUFFER
  622.  
  623. BEGIN
  624. STRING PAD^CHAR;
  625. INT N;
  626. PAD^CHAR := UNCHAR(HIS^PARAMS.PAD^CHAR);
  627. N := UNCHAR(HIS^PARAMS.NPAD);
  628. IF N <> 0 THEN IN^BUF^S ':=' PAD^CHAR FOR 1 & IN^BUF^S FOR N - 1;
  629.  
  630. IF DEBUG^FLAG THEN
  631.     BEGIN
  632.     OUT^PACKET^LENGTH := OUT^PACKET^LENGTH;
  633.     OUT^PACKET ':=' OUT^PACKET[1]
  634.                       FOR OUT^PACKET^LENGTH - 1 & LF;  !STRIP MARK!
  635.     END;
  636.  
  637. IN^BUF^S[N] ':=' OUT^PACKET FOR OUT^PACKET^LENGTH;
  638. RETURN N + OUT^PACKET^LENGTH;
  639. END;
  640.  
  641. INT SUBPROC VALID^MESSAGE;
  642.  
  643. BEGIN
  644. INT LENGTH := 0;
  645.  
  646. !THIS SUBPROC VALIDITY CHECKS THE INCOMING MESSAGE!
  647.  
  648. !  SCAN FOR MARK AND SHIFT MESSAGE TO BEGINNING OF BUFFER!
  649.  
  650. IF DEBUG^FLAG THEN   !ACCEPT INPUT FROM TERMINAL FOR DEBUGGING!
  651.     BEGIN
  652.     PACKET^TYPE := IN^BUF^S;
  653.     IN^PACKET^LENGTH := COUNT - 1;
  654.     IN^BUF^S ':=' IN^BUF^S[1] FOR IN^PACKET^LENGTH & 0;
  655.     IF PACKET^TYPE <> "Y" AND PACKET^TYPE <> "N" THEN
  656.         INPUT^PACKET^NUMBER := PACKET^NUMBER + 1
  657.     ELSE
  658.         INPUT^PACKET^NUMBER := PACKET^NUMBER;
  659.     RETURN TRUE;
  660.     END;
  661.  
  662. WHILE LENGTH < COUNT AND IN^BUF^S[LENGTH] <> MARK DO LENGTH := LENGTH + 1;
  663. COUNT := COUNT - LENGTH;
  664. IF COUNT < 5 THEN RETURN FALSE;   !MARK & LENGTH & SEQ & TYPE & CHECKSUM!
  665. IF LENGTH <> 0 THEN IN^BUF^S ':=' IN^BUF^S[LENGTH] FOR COUNT;
  666.  
  667. LENGTH := 0;
  668. LENGTH.<8:15> := UNCHAR(IN^BUF^S[1]) + 2;
  669.  
  670. IF LENGTH > COUNT THEN RETURN FALSE;
  671.  
  672. CALL CHECKSUMMER(IN^BUF^S,LENGTH);
  673.  
  674. IF SAVE^SUM <> IN^BUF^S[LENGTH - 1] THEN  RETURN FALSE;
  675.  
  676. INPUT^PACKET^NUMBER := UNCHAR(IN^BUF^S[2]);
  677. PACKET^TYPE := IN^BUF^S[3];
  678. IN^PACKET^LENGTH := LENGTH - 5;   !TYPE, SEQ, CHECKSUM, MARK, LENGTH
  679. IN^BUF^S ':=' IN^BUF^S[4] FOR IN^PACKET^LENGTH & 0;
  680. RETURN TRUE;
  681. END;
  682. ?PAGE
  683.  
  684. IF $PARAM(RETRIES)THEN NUM^RETRIES := RETRIES
  685.                   ELSE NUM^RETRIES := MAX^RETRIES;
  686.  
  687.  
  688. WHILE WAIT^FOREVER OR NUM^RETRIES <> 0 DO
  689.     BEGIN
  690.     CALL WRITEREAD(TERMNUM,IN^BUF,PAD^PACKET,512,COUNT);
  691.     IF NOT DEBUG^FLAG THEN CALL AWAITIO(TERMNUM,!BUFFER!,COUNT,,TIME^OUT);
  692.     CALL FILEINFO(TERMNUM,ERROR);
  693.     IF ERROR = 120 THEN ERROR := 0;  !PARITY!
  694.  
  695.     IF ERROR <> 0 THEN
  696.          BEGIN
  697.  
  698.          IF ERROR = 40 THEN   !TIME^OUT!
  699.               BEGIN
  700.               IF NOT WAIT^FOREVER THEN NUM^RETRIES := NUM^RETRIES - 1;
  701.               END;
  702.  
  703.          IF ERROR = 140 THEN  !MODEM ERROR!
  704.               BEGIN
  705.               MODEM^TRIES := MODEM^TRIES + 1;
  706.               IF MODEM^TRIES > MAX^RETRIES THEN
  707.                   BEGIN
  708.                   CALL CONTROL(TERMNUM,12);     !DISCONNECT MODEM!
  709.                   CALL GIVE^IT^UP;
  710.                   END
  711.               ELSE CALL DELAY(100D);     !WAIT 1!
  712.               END;
  713.  
  714.          SCAN NON^FATALS UNTIL ERROR.<8:15>;
  715.          IF $CARRY THEN CALL GIVE^IT^UP; !FATAL!
  716.          END ! I/O ERROR!
  717.  
  718.     ELSE    !NO I/O ERROR!
  719.         BEGIN
  720.         IF VALID^MESSAGE THEN
  721.             IF PACKET^TYPE <> "Y" THEN
  722.                 BEGIN
  723.                 IF PACKET^TYPE <> "N" THEN RETURN;
  724.  
  725.                 ! GOT A NAK, SEE IF IT WAS FOR NEXT PACKET!
  726.                 IF SIX^BIT(PACKET^NUMBER + 1) =
  727.                           INPUT^PACKET^NUMBER THEN
  728.                     !NAK FOR PACKET N + 1 IS EQUIVALENT TO ACK OF N!
  729.                     BEGIN
  730.                     PACKET^TYPE := "Y";
  731.                     INPUT^PACKET^NUMBER := PACKET^NUMBER;
  732.                     RETURN;
  733.                     END;
  734.                 END
  735.             ELSE !ACK!
  736.                 IF INPUT^PACKET^NUMBER = PACKET^NUMBER THEN RETURN;
  737.         IF NOT WAIT^FOREVER THEN NUM^RETRIES := NUM^RETRIES - 1;
  738.         END;
  739.     END; !LOOP!
  740.  
  741. PACKET^TYPE := -1;
  742. RETURN;
  743. END;
  744. ?PAGE "PROC PROCESS^SEND^INIT"
  745. PROC PROCESS^SEND^INIT(IN^PARAMS,TYPE,LENGTH);
  746.  
  747. ! THIS PROC DOES THE HOUSEKEEPING ASSOCIATED WITH SEND INIT MESSAGES
  748. ! AND SEND INIT ACKS
  749. ! IT WILL FORMAT THE MESSAGE INTO OUT^PACKET WITH TYPE "Y" OR "S"
  750. ! DEPENDING ON THE INPUT PARAMETER
  751. !
  752.  
  753. STRING .IN^PARAMS(PARAMS),TYPE;
  754. INT LENGTH;
  755. BEGIN
  756. INT N;
  757. STRING .OUT^PARAMS(PARAMS) := @OUT^PACKET[4];
  758. STRING SAVE^REPEAT;
  759.  
  760. CALL FORMAT^ACK;
  761. OUT^PACKET[3] := TYPE;
  762. IN^PARAMS.CHECK^TYPE := "1";
  763.  
  764. IN^PARAMS.EIGHT^BIT := "N";
  765. OUT^PARAMS ':=' IN^PARAMS FOR LENGTH &
  766.                        DEFAUL[LENGTH] FOR $LEN(PARAMS) - LENGTH;
  767.  
  768. SAVE^REPEAT := OUT^PARAMS.REPEAT^CHAR;
  769. FOR N := 0 TO $LEN(PARAMS) - 1 DO
  770.     IF OUT^PACKET[N + 4] = " " THEN OUT^PACKET[N + 4] := DEFAUL[N];
  771. OUT^PARAMS.REPEAT^CHAR := SAVE^REPEAT;
  772.  
  773. HIS^PARAMS ':=' OUT^PARAMS FOR $LEN(PARAMS);
  774. IF HIS^PARAMS.EIGHT^BIT <> "Y" THEN
  775.     BEGIN
  776.     OUT^PARAMS.EIGHT^BIT := "Y";
  777.     OUT^PARAMS.EIGHT^BIT := "N";    !REMOVE IF YOUR KERMIT WORKS!
  778.     END
  779. ELSE
  780.     BEGIN
  781.     OUT^PARAMS.EIGHT^BIT := MY^PARAMS.EIGHT^BIT;
  782.     HIS^PARAMS.EIGHT^BIT := MY^PARAMS.EIGHT^BIT;
  783.     END;
  784.  
  785. REPEAT^FLAG := (HIS^PARAMS.REPEAT^CHAR <> " ");
  786. MAX^DATA^CHARS := 91;
  787. IF HIS^PARAMS.BUFSIZ <> " " THEN
  788.             MAX^DATA^CHARS := UNCHAR(HIS^PARAMS.BUFSIZ) - 3;
  789.  
  790. LENGTH := $LEN(PARAMS) + 5;
  791. OUT^PACKET[1] := CHAR(LENGTH - 2);
  792. OUT^PACKET^LENGTH := CHECKSUMMER(OUT^PACKET,LENGTH);
  793. RETURN;
  794. END;
  795. ?PAGE "NON IMPLEMENTED"
  796. PROC COMMAND^PROC;
  797. BEGIN
  798. NOT^IMPL^D;
  799. RETURN;
  800. END;
  801. PROC GENERIC^PROC;
  802. BEGIN
  803. IF IN^BUF^S = "L" THEN
  804.     BEGIN
  805.     MAX^RETRIES := 2;
  806.     CALL FORMAT^ACK;
  807.     CALL GET^PACKET;
  808.     CALL GIVE^IT^UP(TRUE);
  809.     END;
  810. NOT^IMPL^D;
  811. RETURN;
  812. END;
  813. ?PAGE "PROC RECEIVE PROC"
  814. INT PROC RECEIVE^PROC;
  815.  
  816. ! IN BOUND FILE PROC !
  817.  
  818.  
  819. BEGIN
  820. LEGAL^PACKETS^D("BSFDZ");
  821. INT OUT^COUNT,            !INPUT PARSING STUFF!
  822.     LF^WAIT := FALSE,
  823.     WRITE^IT^OUT := FALSE;
  824. STRING CHRSAV,CHR;
  825. LITERAL MAX^BUF^SIZE = 132;
  826.  
  827.  
  828.  
  829. SUBPROC PARSE^FILE^HEADER;
  830. BEGIN
  831. INT NAME^LENGTH;
  832. STRING .IN^POINTER;
  833.  
  834. RSCAN IN^BUF^S[IN^PACKET^LENGTH -1] WHILE "~" -> @IN^POINTER;
  835.  
  836. IN^PACKET^LENGTH := @IN^POINTER[1] '-' @IN^BUF^S;
  837.  
  838. IF IN^PACKET^LENGTH <= 0 THEN
  839.     BEGIN
  840.     CALL ERROR^PACKET(999);    !ERROR!
  841.     RETURN;
  842.     END;
  843.  
  844. IN^POINTER[1] := 0;
  845.  
  846. SCAN IN^BUF^S UNTIL "~" -> @IN^POINTER;
  847.  
  848. IF NOT $CARRY THEN
  849.     BEGIN
  850.     NAME^LENGTH := IN^PACKET^LENGTH '-'
  851.                           (@IN^POINTER[1] '-' @IN^BUF^S);
  852.     IN^BUF^S ':=' IN^POINTER[1] FOR NAME^LENGTH & 0;
  853.     END
  854. ELSE
  855.     BEGIN     !NOT A SUPPLIED NAME, PLAY WITH REMOTE NAME!
  856.     NAME^LENGTH := IN^PACKET^LENGTH;
  857.     SCAN IN^BUF^S UNTIL "."  -> @IN^POINTER;
  858.     IF NOT $CARRY THEN
  859.          BEGIN
  860.          IF TRUNC^FLAG OR IN^POINTER[1] = 0 THEN IN^POINTER := 0;
  861.          IF FLIP^FLAG AND IN^POINTER <> 0 THEN
  862.               BEGIN
  863.               IN^POINTER := 0;
  864.               SBUF ':=' IN^BUF^S FOR NAME^LENGTH;
  865.               IN^BUF^S ':=' IN^POINTER[1] FOR NAME^LENGTH;
  866.               SCAN IN^BUF^S UNTIL 0 -> @IN^POINTER;
  867.               IN^POINTER ':=' "." & SBUF FOR NAME^LENGTH;
  868.               END;
  869.            END;
  870.          END;
  871.  
  872. CALL FNAMEEXPAND(IN^BUF,FILE^NAME,DEFAULT^VOL);
  873.  
  874. RETURN;
  875. END;    !SUBPROC!
  876.  
  877.  
  878. SUBPROC GET^NEXT^CHAR;
  879. BEGIN
  880. CHRSAV := 0;
  881. IF IN^BUF^S[N] = HIS^PARAMS.EIGHT^BIT AND HIS^PARAMS.EIGHT^BIT <> "N" THEN
  882.     BEGIN    !EIGHT BIT!
  883.     N := N + 1;
  884.     CHRSAV := %200;
  885.     END;
  886. IF IN^BUF^S[N] = HIS^PARAMS.QUOTE^CHAR THEN
  887.     BEGIN
  888.     N := N + 1;
  889.     CHR := IN^BUF^S[N];
  890.     IF (CHR = HIS^PARAMS.EIGHT^BIT AND HIS^PARAMS.EIGHT^BIT <> "N") OR
  891.         (REPEAT^FLAG AND CHR = HIS^PARAMS.REPEAT^CHAR) OR
  892.         CHR = HIS^PARAMS.QUOTE^CHAR THEN !TAKE IT AS LITERAL!
  893.         CHRSAV := CHRSAV + CHR
  894.         ELSE CHRSAV := CTL(CHRSAV + CHR);
  895.     END
  896. ELSE CHRSAV := CHRSAV + IN^BUF^S[N];
  897.  
  898. N := N + 1;
  899.  
  900. RETURN;
  901.  
  902. END;
  903.  
  904.  
  905. INT SUBPROC PROCESS^OUTPUT^DATA;
  906.  
  907. BEGIN
  908.  
  909. INT  REPEAT^COUNT := 0;
  910.  
  911. N := 0;
  912. WHILE N < IN^PACKET^LENGTH OR REPEAT^COUNT <> 0 DO
  913.     BEGIN
  914.     IF OUT^COUNT >= MAX^BUF^SIZE OR WRITE^IT^OUT THEN
  915.         IF NOT WRITE^OUTPUT^FILE(OUT^COUNT) THEN RETURN FALSE
  916.         ELSE
  917.             BEGIN
  918.             OUT^COUNT := 0;
  919.             WRITE^IT^OUT := FALSE;
  920.             END;
  921.  
  922.     IF REPEAT^COUNT <> 0 THEN
  923.         BEGIN
  924.         SBUF[OUT^COUNT] := CHRSAV;
  925.         OUT^COUNT := OUT^COUNT + 1;
  926.         REPEAT^COUNT := REPEAT^COUNT - 1;
  927.         END
  928.  
  929.     ELSE
  930.         BEGIN  !REPEAT COUNT EXHAUSTED, GET ANOTHER CHARACTER!
  931.         CHR := IN^BUF^S[N];
  932.         IF NOT REPEAT^FLAG OR CHR <> HIS^PARAMS.REPEAT^CHAR THEN
  933.             REPEAT^COUNT := 1
  934.         ELSE
  935.             BEGIN
  936.             REPEAT^COUNT := UNCHAR(IN^BUF^S[N+1]);
  937.             N := N + 2;
  938.             END;
  939.  
  940.         CALL GET^NEXT^CHAR;
  941.  
  942.         IF CHRSAV = LF AND LF^WAIT THEN
  943.             BEGIN
  944.             LF^WAIT := FALSE;
  945.             REPEAT^COUNT := REPEAT^COUNT - 1;
  946.             WRITE^IT^OUT := TRUE;
  947.             END;
  948.         IF LF^WAIT AND CHRSAV <> LF THEN
  949.             BEGIN
  950.             SBUF[OUT^COUNT] := CR;
  951.             OUT^COUNT := OUT^COUNT + 1;
  952.             LF^WAIT := FALSE;
  953.             END;
  954.         IF CHRSAV = CR THEN
  955.             BEGIN
  956.             REPEAT^COUNT := REPEAT^COUNT - 1;
  957.             LF^WAIT := TRUE;
  958.             END;
  959.         IF CHRSAV = TAB AND TABS^FLAG THEN
  960.             BEGIN
  961.             REPEAT^COUNT := 8 * REPEAT^COUNT - (OUT^COUNT LAND 7);
  962.             CHRSAV := " ";
  963.             END;
  964.         END; !NEW CHARACTER, REPEAT COUNT = 0!
  965.  
  966.     END; !LOOP!
  967.  
  968. RETURN TRUE;
  969. END; !SUBPROC!
  970.  
  971. SUBPROC RECEIVE^FILE^HEADER;
  972. BEGIN
  973. LEGAL^PACKETS^D("SZBF");
  974. WHILE 1 DO
  975.     BEGIN
  976.     CHECK^LEGAL^D;
  977.     CASE N OF
  978.         BEGIN
  979. !0!     BEGIN
  980.         CALL PROCESS^SEND^INIT(IN^BUF^S,"YY",IN^PACKET^LENGTH);
  981.         CALL GET^PACKET;
  982.         END;
  983.  
  984. !1!     BEGIN  !EOF!
  985.         CALL FORMAT^ACK;
  986.         CALL GET^PACKET;
  987.         END;
  988.  
  989. !2!     RETURN; !BREAK!
  990.  
  991. !3!     BEGIN   !FILE HEADER!
  992.         CALL PARSE^FILE^HEADER;
  993.         IF NOT OPEN^OUTPUT^FILE THEN BAD^PACKET^D;
  994.         OUT^COUNT := 0;
  995.         RETURN;
  996.         END;
  997.  
  998.         OTHERWISE BAD^PACKET^D;
  999.  
  1000.         END; !CASE!
  1001.     END; !LOOP!
  1002. END;    !SUBPROC!
  1003.  
  1004.  
  1005. SUBPROC RECEIVE^DATA;
  1006. BEGIN
  1007. LEGAL^PACKETS^D("FZD");
  1008. WHILE 1 DO
  1009.     BEGIN
  1010.     CHECK^LEGAL^D;
  1011.     CASE N OF
  1012.         BEGIN
  1013. !0!     BEGIN  !FILE HEADER!
  1014.         CALL FORMAT^ACK;
  1015.         CALL GET^PACKET;
  1016.         END;
  1017.  
  1018. !1!     BEGIN   !EOF!
  1019.         IF OUT^COUNT <> 0 AND NOT WRITE^OUTPUT^FILE(OUT^COUNT)
  1020.             THEN BAD^PACKET^D;
  1021.         OUT^COUNT := 0;
  1022.         CLOSE^FILE^D(FILE^FCB);
  1023.         RETURN;
  1024.         END;
  1025.  
  1026. !2!     BEGIN   !DATA PACKET!
  1027.         IF INPUT^PACKET^NUMBER <> PACKET^NUMBER THEN
  1028.             BEGIN   !IF INCREMENT > 1, THINGS ARE FUBAR, SO PROCEED WITH DATA!
  1029.             IF NOT PROCESS^OUTPUT^DATA THEN BAD^PACKET^D; !HANDLES OWN ERRORS!
  1030.             END;
  1031.         CALL FORMAT^ACK;
  1032.         CALL GET^PACKET;
  1033.         RETURN;
  1034.         END;
  1035.  
  1036.         OTHERWISE BAD^PACKET^D;
  1037.  
  1038.         END; !CASE!
  1039.     END; !LOOP!
  1040. END;    !SUBPROC!
  1041.  
  1042.  
  1043.  
  1044.  
  1045.  
  1046.  
  1047.  
  1048. WHILE 1 DO
  1049.     BEGIN
  1050.     CHECK^LEGAL^D;
  1051.     CASE N OF
  1052.         BEGIN
  1053.         !CASE 0 COMPLETE (BREAK)!
  1054.            BEGIN
  1055.            IF OUT^COUNT <> 0 THEN CALL WRITE^OUTPUT^FILE(OUT^COUNT);
  1056.            OUT^COUNT := 0;
  1057.            CLOSE^FILE^D(FILE^FCB);
  1058.            CALL FORMAT^ACK;
  1059.            CALL GET^PACKET;
  1060.            RETURN TRUE;
  1061.            END;
  1062.  
  1063.         !CASE 1! !SEND  INIT!       CALL RECEIVE^FILE^HEADER;
  1064.         !CASE 2! !FILE HEADER!      CALL RECEIVE^DATA;
  1065.         !CASE 3! !DATA!             CALL RECEIVE^DATA;
  1066.         !CASE 4! !EOF!              CALL RECEIVE^FILE^HEADER;
  1067.  
  1068.         !ABORT! OTHERWISE           RETURN FALSE;
  1069.         END; !CASE!
  1070.     END; !LOOP!
  1071. END;  !PROC!
  1072. ?PAGE "PROC SEND^PROC"
  1073. PROC SEND^PROC;
  1074.  
  1075. !THIS PROC HANDLES OUT BOUND FILE TRANSFERS!
  1076.  
  1077.  
  1078. BEGIN
  1079.  
  1080. INT ERROR,.COUNT^READ[0:0],OUT^COUNT,MAX^COUNT,SHIP^FLAG;
  1081.  
  1082. INT OUT^OF^INPUT := TRUE,DONE := FALSE;
  1083.  
  1084. INT REPEAT^COUNT, EIGHT^BIT, CONTROL^CHAR, NEED^QUOTE, TOTAL^COUNT;
  1085. STRING CHR;
  1086.  
  1087. STRING .IN^POINTER,.OUT^POINTER;
  1088.  
  1089. STRING .ECHO^FILE^NAME[0:39],.TEMP^NAME[0:39];
  1090.  
  1091. INT ECHO^NAME^LENGTH;
  1092. INT REMAINING^CHARS,IN^COUNT;
  1093.  
  1094.  
  1095. DEFINE CHECK^PACKET^D =
  1096.     IF PACKET^TYPE <> "Y" THEN
  1097.         BEGIN
  1098.         ERROR^MESSAGE^D("Gave up waiting for ACK");
  1099.         RETURN;
  1100.         END#;
  1101.  
  1102.  
  1103.  
  1104. INT SUBPROC PARSE^FILE^HEADER;
  1105. BEGIN
  1106.  
  1107. RSCAN IN^BUF^S[IN^PACKET^LENGTH -1] WHILE "~" -> @IN^POINTER;
  1108.  
  1109. IN^PACKET^LENGTH := @IN^POINTER[1] '-' @IN^BUF^S;
  1110.  
  1111. IF IN^PACKET^LENGTH <= 0 THEN
  1112.     BEGIN
  1113.     CALL ERROR^PACKET(11);    !FILE NO EXIST!
  1114.     RETURN FALSE;
  1115.     END;
  1116.  
  1117. IN^POINTER[1] := 0;
  1118.  
  1119. SCAN IN^BUF^S UNTIL "~" -> @IN^POINTER;
  1120.  
  1121. IF NOT $CARRY THEN
  1122.     BEGIN
  1123.     ECHO^NAME^LENGTH := IN^PACKET^LENGTH '-'
  1124.                           (@IN^POINTER[1] '-' @IN^BUF^S);
  1125.     ECHO^FILE^NAME ':=' IN^POINTER[1] FOR ECHO^NAME^LENGTH;
  1126.     END
  1127. ELSE
  1128.     BEGIN
  1129.     ECHO^NAME^LENGTH := IN^PACKET^LENGTH;
  1130.     ECHO^FILE^NAME ':=' IN^BUF^S FOR ECHO^NAME^LENGTH;
  1131.     END;
  1132.  
  1133. CALL FNAMEEXPAND(IN^BUF,FILE^NAME,DEFAULT^VOL);
  1134.  
  1135. RETURN TRUE;
  1136. END;    !SUBPROC!
  1137.  
  1138.  
  1139. SUBPROC SET^UP^DATA;
  1140. BEGIN
  1141. OUT^COUNT := 0;
  1142. OUT^PACKET ':=' [MARK,"LND"];
  1143. @OUT^POINTER := @OUT^PACKET[4];
  1144. SHIP^FLAG := FALSE;
  1145. REMAINING^CHARS := MAX^DATA^CHARS;
  1146. RETURN;
  1147. END;    !SUBPROC!
  1148.  
  1149. SUBPROC GET^REPEAT(POINTER);
  1150. STRING .POINTER;
  1151. BEGIN
  1152. STRING .LOCAL^POINTER;
  1153. STRING CHR1;
  1154. IF TOTAL^COUNT + 2 > REMAINING^CHARS THEN RETURN;
  1155. IF COUNT^READ - IN^COUNT < 4 THEN RETURN;
  1156. @LOCAL^POINTER := @POINTER[1];
  1157. CHR1 := POINTER;
  1158. IN^COUNT := IN^COUNT + 1;    !MUST SUBTRACT OFF LATER!
  1159. WHILE CHR1 = LOCAL^POINTER[REPEAT^COUNT] AND IN^COUNT < COUNT^READ DO
  1160.     BEGIN
  1161.     IN^COUNT := IN^COUNT + 1;
  1162.     REPEAT^COUNT := REPEAT^COUNT + 1;
  1163.     END;
  1164.  
  1165. IF REPEAT^COUNT < 3 THEN
  1166.     BEGIN  !DON'T BOTHER!
  1167.     IN^COUNT := IN^COUNT - REPEAT^COUNT;
  1168.     REPEAT^COUNT := 0;
  1169.     END
  1170. ELSE REPEAT^COUNT := REPEAT^COUNT + 1;
  1171.  
  1172. IN^COUNT := IN^COUNT - 1;   !COMPENSATE!
  1173. END;
  1174.  
  1175.  
  1176. INT SUBPROC FILL^IN^DATA;
  1177.  
  1178. ! THIS IS THE MAIN INPUT AND OUTPUT BUFFERING SUBPROC !
  1179.  
  1180. BEGIN
  1181.  
  1182. STRING REPEAT;
  1183.  
  1184. WHILE REMAINING^CHARS > 0 DO
  1185.     BEGIN
  1186.     IF IN^COUNT = COUNT^READ THEN
  1187.         BEGIN
  1188.         OUT^OF^INPUT := TRUE;
  1189.         RETURN FALSE;
  1190.         END;
  1191.  
  1192.     REPEAT^COUNT := 0;
  1193.     EIGHT^BIT := 0;
  1194.     CONTROL^CHAR := 0;
  1195.     NEED^QUOTE := 0;
  1196.  
  1197.     CHR := SBUF[IN^COUNT];
  1198.  
  1199.     IF ((CHR LAND %200) <> 0) AND HIS^PARAMS.EIGHT^BIT <> "N" THEN
  1200.         EIGHT^BIT := 1;
  1201.  
  1202.     CHR := CHR LAND %177;
  1203.  
  1204.     IF CHR < " " THEN
  1205.         BEGIN
  1206.         CHR := CTL(CHR);
  1207.         CONTROL^CHAR := 1;
  1208.         END;
  1209.  
  1210.     IF (REPEAT^FLAG AND CHR = HIS^PARAMS.REPEAT^CHAR) OR
  1211.         CHR = HIS^PARAMS.QUOTE^CHAR OR
  1212.         (HIS^PARAMS.EIGHT^BIT <> "N" AND CHR = HIS^PARAMS.EIGHT^BIT) THEN
  1213.         NEED^QUOTE := 1;
  1214.  
  1215.     TOTAL^COUNT := NEED^QUOTE + CONTROL^CHAR + EIGHT^BIT + 1;
  1216.  
  1217.     IF TOTAL^COUNT > REMAINING^CHARS THEN RETURN TRUE;  !SHIP IT!
  1218.  
  1219.     IF REPEAT^FLAG THEN CALL GET^REPEAT(SBUF[IN^COUNT]);
  1220.  
  1221.     IF REPEAT^COUNT <> 0 THEN
  1222.         BEGIN
  1223.         REPEAT := CHAR(REPEAT^COUNT.<8:15>);
  1224.         TOTAL^COUNT := TOTAL^COUNT + 2;
  1225.         OUT^POINTER ':=' HIS^PARAMS.REPEAT^CHAR FOR 1
  1226.                           & REPEAT FOR 1 -> @OUT^POINTER;
  1227.         END;
  1228.  
  1229.     IF EIGHT^BIT <> 0 THEN
  1230.         OUT^POINTER ':=' HIS^PARAMS.EIGHT^BIT FOR 1 -> @OUT^POINTER;
  1231.  
  1232.     IF CONTROL^CHAR <> 0 THEN
  1233.         OUT^POINTER ':=' HIS^PARAMS.QUOTE^CHAR FOR 1 -> @OUT^POINTER;
  1234.  
  1235.     IF NEED^QUOTE <> 0 THEN
  1236.         OUT^POINTER ':=' HIS^PARAMS.QUOTE^CHAR FOR 1 -> @OUT^POINTER;
  1237.  
  1238.     !AND FINALLY THE CHARACTER!
  1239.     OUT^POINTER ':=' CHR FOR 1 -> @OUT^POINTER;
  1240.  
  1241.     IN^COUNT := IN^COUNT + 1;
  1242.  
  1243.     REMAINING^CHARS := REMAINING^CHARS - TOTAL^COUNT;
  1244.  
  1245.     OUT^COUNT := OUT^COUNT + TOTAL^COUNT;
  1246.  
  1247.     END; !WHILE LOOP ON FULL OUTPUT BUFFER!
  1248.  
  1249. RETURN TRUE;   !GO SHIP IT!
  1250. END;    !SUBPROC!
  1251.  
  1252.  
  1253. IF NOT PARSE^FILE^HEADER THEN RETURN;
  1254.  
  1255. IF NOT OPEN^INPUT^FILE THEN RETURN;
  1256.  
  1257. CALL PROCESS^SEND^INIT(MY^PARAMS,"SS",$LEN(PARAMS));
  1258. MY^PARAMS ':=' DEFAUL FOR $LEN(PARAMS);         !RESET DEFAULTS!
  1259. CALL GET^PACKET;
  1260.  
  1261. CHECK^PACKET^D;
  1262.  
  1263. ! GOT SEND INIT REPLY, PROCESS IT. !
  1264. CALL PROCESS^SEND^INIT(IN^BUF^S,"YY",IN^PACKET^LENGTH);
  1265.  
  1266.  
  1267. ! SET UP FILE HEADER !
  1268.  
  1269. OUT^PACKET ':=' [MARK,"LNF"] & ECHO^FILE^NAME FOR ECHO^NAME^LENGTH;
  1270. CALL FINISH^AND^SHIP(ECHO^NAME^LENGTH);
  1271.  
  1272. CHECK^PACKET^D;
  1273.  
  1274. CALL SET^UP^DATA;   !INITIALIZE OUTPUT STRUCTURE!
  1275.  
  1276. WHILE NOT DONE DO
  1277.     BEGIN
  1278.     IF OUT^OF^INPUT THEN
  1279.         BEGIN
  1280.         ERROR := READ^FILE(FILE^FCB,BUF,COUNT^READ);
  1281.         IF ERROR = 1 THEN  !EOF!
  1282.             BEGIN
  1283.             DONE := TRUE;
  1284.             IF OUT^COUNT <> 0 THEN SHIP^FLAG := TRUE;
  1285.             END
  1286.         ELSE
  1287.             IF ERROR <> 0 THEN
  1288.             BEGIN
  1289.             CALL ERROR^PACKET(ERROR);
  1290.             RETURN;
  1291.             END;
  1292.         IF ERROR = 0 THEN @IN^POINTER := @IN^BUF^S;
  1293.  
  1294.         OUT^OF^INPUT := FALSE;
  1295.         SBUF[COUNT^READ] ':=' [CR,LF];
  1296.         COUNT^READ := COUNT^READ + 2;
  1297.         IN^COUNT := 0;
  1298.     END;
  1299.  
  1300.     WHILE NOT OUT^OF^INPUT DO IF SHIP^FLAG OR FILL^IN^DATA THEN
  1301.         BEGIN
  1302.         CALL FINISH^AND^SHIP(OUT^COUNT);
  1303.         CHECK^PACKET^D;
  1304.         CALL SET^UP^DATA;
  1305.         END;
  1306.  
  1307.     IF ERROR = 1 THEN
  1308.         BEGIN
  1309.         CLOSE^FILE^D(FILE^FCB);
  1310.  
  1311.         OUT^PACKET ':=' [MARK,"LNZ"];           !EOF PACKET!
  1312.         CALL FINISH^AND^SHIP(0);
  1313.         CHECK^PACKET^D;
  1314.  
  1315.         OUT^PACKET ':=' [MARK,"LNB"];           !BREAK PACKET!
  1316.         CALL FINISH^AND^SHIP(0);
  1317.         CHECK^PACKET^D;
  1318.  
  1319.         END;
  1320.  
  1321.  
  1322.     END; !LOOP!
  1323. END;  !PROC!
  1324. ?PAGE "MAIN PROC"
  1325. PROC KERMIT^SERVER MAIN;
  1326.  
  1327. BEGIN
  1328. LEGAL^PACKETS^D("SIRGCNYE");
  1329. INT DONT^NAK;
  1330.  
  1331. CALL INITIALIZE;
  1332. DONT^NAK := FALSE;
  1333.  
  1334.  
  1335. WHILE 1 DO BEGIN
  1336.     IF DONT^NAK THEN DONT^NAK := FALSE ELSE CALL FORMAT^NAK;
  1337.     WAIT^FOREVER := TRUE;
  1338.     CALL GET^PACKET;
  1339.     CHECK^LEGAL^D;
  1340.     WAIT^FOREVER := FALSE;
  1341.     CASE N OF
  1342.         BEGIN
  1343. !S!     CALL RECEIVE^PROC;
  1344. !I!     BEGIN
  1345.             CALL PROCESS^SEND^INIT(IN^BUF^S,"YY",IN^PACKET^LENGTH);
  1346.             DONT^NAK := TRUE;
  1347.         END;
  1348. !R!     CALL SEND^PROC;
  1349. !G!     CALL GENERIC^PROC;
  1350. !C!     CALL COMMAND^PROC;
  1351. !NAK!   ;
  1352. !ACK!   ;
  1353. !ERROR! ;
  1354.         OTHERWISE
  1355.             ERROR^MESSAGE^D("Unexpected Packet Received");
  1356.         END; !CASE!
  1357.         CLOSE^FILE^D(FILE^FCB);
  1358.     END; !FOREVER LOOP!
  1359. END;!MAIN!
  1360.