MODULE KMT_PP_MODULE;
 
@******************************************************************************@
@*                                                                            *@
@* Mode definitions                                                           *@
@*                                                                            *@
@******************************************************************************@
 
MODE
KMT_TRACE_FLAGS_S IS WORD STRUCT (
   BIT    PH_TRACING,
          PP_TRACING,
          FH_TRACING,
          DH_TRACING,
   28-BIT SPARE);
 
MODE
KMT_FH_RECORD_DETAILS_S IS STRUCT (
   BOOL        FILE_OPEN,
               NEW_RECORD,
               END_OF_FILE,
   WORD        TEXT_TYPE,                        @ 0 = EBCDIC                  @
                                                 @ 1 = ASCII                   @
                                                 @ 2 = BINARY                  @
   INT         MAX_RECORD_LENGTH,
               RECORD_LENGTH,
   (4098) BYTE RECORD);                          @ Maximum record size of 4096 @
                                                 @ plus 2 bytes for CRLF pair  @
                                                 @ when constructing output    @
                                                 @ records                     @
 
MODE
KMT_DH_DEVICE_DETAILS_S IS STRUCT (
  BOOL FILE_OPEN,
  WORD MAX_INPUT_LENGTH,
       MAX_OUTPUT_LENGTH,
       INPUT_PARITY,
       OUTPUT_PARITY,
       PAUSE);
 
MODE
KMT_PP_PACKET_DATA_S IS STRUCT (
   INT       DATA_LENGTH,
   (91) BYTE DATA);
 
MODE
KMT_PP_CONFG_PARAMS_S IS STRUCT (
   BYTE   MARK,
          MAXL,
          TIME,
          NPAD,
          PADC,
          EOL,
          QCTL,
          QBIN,
          CHKT,
          REPT,
   4-BYTE CAPAS);
 
MODE
KMT_PP_PACKET_STATISTICS_S IS STRUCT (
   INT INPUT_TOTAL,
       OUTPUT_TOTAL);
 
MODE
KMT_MTM_VALUES IS ANY (
   LONG WORD          LW_VALUE,
   LONG INT           LI_VALUE,
   REF WORD           RW_VALUE,
   REF INT            RI_VALUE,
   REF LONG WORD      RLW_VALUE,
   REF LONG INT       RLI_VALUE,
   REF () BYTE        RVB_VALUE,
   REF () REF () BYTE RVRVB_VALUE);
 
***PAGE
 
@******************************************************************************@
@*                                                                            *@
@* External procedure references                                              *@
@*                                                                            *@
@******************************************************************************@
 
EXT
PROC
   (REF () BYTE,                                 @ OUTPUT_PACKET               @
    BOOL,                                        @ PROMPT                      @
    RESPONSE                                     @ RESPONSE                    @
   )                                   KMT_DH_OUTPUT;
 
EXT
PROC
   (REF () BYTE,                                 @ INPUT_BUFFER                @
    REF INT,                                     @ INPUT_BUFFER_LENGTH         @
    RESPONSE                                     @ RESPONSE                    @
   )                                   KMT_DH_INPUT;
 
EXT
PROC
   (INT,                                         @ RESULT_CODE                 @
    WORD,                                        @ DESTINATION                 @
    REF () KMT_MTM_VALUES,                       @ PARAMS                      @
    LONG WORD,                                   @ PE_CONTINGENCY_MESSAGE      @
    BOOL,                                        @ DUMP                        @
    BOOL                                         @ UNRECOVERABLE               @
   )                                   KMT_EH_LOG_ERROR;
 
EXT
PROC
   (
    RESPONSE                                     @ RESPONSE                    @
   )                                   KMT_FH_READ;
 
EXT
PROC
   (
    RESPONSE                                     @ RESPONSE                    @
   )                                   KMT_FH_WRITE;
 
EXT
PROC
   (
    WORD,                                        @ CHAR                        @
    WORD,                                        @ I/O FLAG                    @
    RESPONSE                                     @ RESPONSE                    @
   )                                  KMT_SP_CHECK_VME_CHAR;
 
EXT
PROC
   (
    INT,                                         @ TYPE                        @
    REF () KMT_MTM_VALUES                        @ PARAMS                      @
   )                                   KMT_SP_LOG_TRACE_MESSAGE;
 
 
***PAGE
 
@******************************************************************************@
@*                                                                            *@
@* External data references                                                   *@
@*                                                                            *@
@******************************************************************************@
 
@ Constants: @
@ ********** @
 
EXT (256) BYTE
   EBCDIC_TO_ASCII,                                       @ translation tables @
   ASCII_TO_EBCDIC;
 
EXT (26) INT
   PACKET_CODES;         @ packet codes to protocol handler state table events @
 
EXT INT
   BAD_PKT,INVALID_PKT,UNS_PKT,NON_PKT;
 
***LINES(4)
 
@ Variables: @
@ ********** @
 
EXT (<CASCADE>)
REF KMT_TRACE_FLAGS_S KMT_TRACE_FLAGS;
 
EXT (<CASCADE>)
REF KMT_FH_RECORD_DETAILS_S KMT_FH_RECORD_DETAILS;
 
EXT (<CASCADE>)
REF KMT_DH_DEVICE_DETAILS_S KMT_DH_DEVICE_DETAILS;
 
EXT (<CASCADE>)
REF KMT_PP_CONFG_PARAMS_S KMT_PP_LOCAL_CONFG_PARAMS;
 
EXT (<CASCADE>)
REF KMT_PP_CONFG_PARAMS_S KMT_PP_REMOTE_CONFG_PARAMS;
 
EXT (<CASCADE>)
REF KMT_PP_PACKET_STATISTICS_S KMT_PP_PACKET_STATISTICS;
 
EXT (<CASCADE>)
REF REF () BYTE KMT_PH_INPUT_PACKET_DATA;
 
***LINES(4)
 
@ Results: @
@ ******** @
 
***LINES(4)
 
***PAGE
 
@******************************************************************************@
@*                                                                            *@
@* Static data declarations.                                                  *@
@*                                                                            *@
@******************************************************************************@
 
@ Constants: @
@ ********** @
 
STATIC WORD
   EBCDIC IS 0,                                               @ transfer codes @
   ASCII  IS 1,
   BINARY IS 2;
 
STATIC INT
   KMT_PP_PACKET_IN_MSG       IS 251,
   KMT_PP_PACKET_OUT_MSG      IS 252,
   KMT_PP_PACKET_DATA_IN_MSG  IS 253,
   KMT_PP_PACKET_DATA_OUT_MSG IS 254,
   KMT_PP_PARAM_OUT_MSG       IS 258,
   KMT_PP_PARAM_IN_MSG        IS 259;
 
***LINES(4)
 
@ Variables: @
@ ********** @
 
STATIC
KMT_PP_PACKET_DATA_S KMT_PP_INPUT_PACKET_DATA;
 
STATIC
KMT_PP_PACKET_DATA_S KMT_PP_OUTPUT_PACKET_DATA;
 
STATIC
REF () BYTE KMT_PP_RECALL_DATA;
 
***LINES(4)
 
@ Results: @
@ ******** @
 
***LINES(4)
 
***PAGE
 
@******************************************************************************@
@*                                                                            *@
@* Procedure declarations                                                     *@
@*                                                                            *@
@******************************************************************************@
 
STATIC
PROC
   KMT_PP_CHECK_COMPLETE IS (
   INT             DATA_LENGTH,
   REF REF () BYTE BUFFER) BOOL:
 
@******************************************************************************@
@*                                                                            *@
@* This procedure is used to update a buffer pointer and to check if the      *@
@* processing of BUFFER is complete.                                          *@
@* The procedure returns TRUE if the processing of BUFFER has been completed, *@
@* FALSE otherwise.                                                           *@
@*                                                                            *@
@******************************************************************************@
 
BEGIN
   IF
      LENGTH BUFFER GT DATA_LENGTH
 
   THEN                                          @ Incomplete, update pointer  @
      BUFFER := BUFFER(DATA_LENGTH::);
      FALSE
 
   ELSE                                          @ Complete return ZLR         @
      BUFFER := BUFFER(SIZE 0);
      TRUE
   FI
END;                                   @ KMT_PP_CHECK_COMPLETE                 @
 
***PAGE
 
STATIC
PROC
   KMT_PP_MOVE IS (
   () BYTE         SOURCE,
   REF REF () BYTE DESTINATION,
   REF BOOL        COMPLETE) INT:
 
@******************************************************************************@
@*                                                                            *@
@* This procedure is used to move the source string into the destination      *@
@* buffer and to check if the destination buffer is full.                     *@
@* The procedure returns the number of characters moved.                      *@
@*                                                                            *@
@******************************************************************************@
 
BEGIN
   INT S_LEN IS LENGTH SOURCE,
       D_LEN IS LENGTH DESTINATION;
 
   IF
      S_LEN GT D_LEN
 
   THEN                                          @ Insufficient space in       @
                                                 @ buffer for string           @
      UNLESS
         COMPLETE IS NIL
 
      THEN
         COMPLETE := TRUE;
      FI;
 
      0
   ELSE
      DESTINATION(SIZE S_LEN) := SOURCE;
 
      UNLESS
         COMPLETE IS NIL
 
      THEN
         COMPLETE := KMT_PP_CHECK_COMPLETE (S_LEN,
                                            DESTINATION);
      FI;
 
      S_LEN
   FI
END;                                   @ KMT_PP_MOVE                           @
 
***PAGE
 
STATIC
PROC
   KMT_PP_GENERATE_CHECKSUM IS (
   REF () BYTE PACKET) BYTE:
 
@******************************************************************************@
@*                                                                            *@
@* This procedure is used to generate a checksum for the data contained in    *@
@* the packet.                                                                *@
@*                                                                            *@
@******************************************************************************@
 
BEGIN
   WORD SUM := 0;
 
   FOR  I
   FROM 1                                        @ 'MARK' character not        @
                                                 @ included in checksum        @
   TO   PACKET(1) - X"20"
   DO
      SUM := SUM + PACKET(I)
   REPEAT;
 
   ((SUM + ((SUM & X'C0') SCALE -6)) & X'3F') + X"20"
END;                                   @ KMT_PP_GENERATE_CHECKSUM              @
 
***PAGE
 
STATIC
PROC
   KMT_PP_LOG_TRACE_MESSAGE IS (
   INT         TYPE,
   REF () BYTE PACKET):
 
@******************************************************************************@
@*                                                                            *@
@* This procedure is used to log the packet contents to the job journal.      *@
@*                                                                            *@
@******************************************************************************@
 
BEGIN
   INT PACKET_LENGTH IS LENGTH PACKET;
 
   INT LEN := PACKET(1) - X"20",
       SEQ := PACKET(2) - X"20",
       TYP := PACKET_CODES(PACKET(3) - X"41");
 
   () KMT_MTM_VALUES PARAMS := (PACKET           @ COMPLETE PACKET             @
                                AS KMT_MTM_VALUES.RVB_VALUE,
                                PACKET(0)        @ MARK                        @
                                AS KMT_MTM_VALUES.RVB_VALUE,
                                LEN              @ LEN                         @
                                AS KMT_MTM_VALUES.RI_VALUE,
                                SEQ              @ SEQ                         @
                                AS KMT_MTM_VALUES.RI_VALUE,
                                TYP              @ TYPE                        @
                                AS KMT_MTM_VALUES.RI_VALUE,
                                PACKET(4 SIZE PACKET_LENGTH - 5)
                                                 @ DATA                        @
                                AS KMT_MTM_VALUES.RVB_VALUE,
                                PACKET(PACKET_LENGTH - 1)
                                                 @ CHECKSUM                    @
                                AS KMT_MTM_VALUES.RVB_VALUE);
 
   KMT_SP_LOG_TRACE_MESSAGE (TYPE,
                             PARAMS)
END;                                   @ KMT_PP_LOG_TRACE_MESSAGE              @
 
***PAGE
 
STATIC
PROC
   KMT_PP_DATA_TRACE_MESSAGE IS (
   INT         TYPE,
   INT         CODE,
   REF () BYTE DATA):
 
@******************************************************************************@
@*                                                                            *@
@* This procedure is used to log the packet data to the job journal.          *@
@*                                                                            *@
@******************************************************************************@
 
BEGIN
   INT CHAR_CODE := CODE;
   () KMT_MTM_VALUES PARAMS := (CHAR_CODE        @ character code of data      @
                                AS KMT_MTM_VALUES.RI_VALUE,
                                DATA             @ data                        @
                                AS KMT_MTM_VALUES.RVB_VALUE);
 
   KMT_SP_LOG_TRACE_MESSAGE (TYPE,
                             PARAMS)
END;                                   @ KMT_PP_DATA_TRACE_MESSAGE             @
 
***PAGE
 
STATIC
PROC
   KMT_PP_BUILD_PACKET_DATA IS (
                REF REF () BYTE BUFFER,
                REF REF () BYTE DATA,
                WORD            TEXT_TYPE,
                REF BOOL        BUFFER_COMPLETE) BOOL:
 
@******************************************************************************@
@*                                                                            *@
@* This procedure is used to perform data translation of the data area        *@
@* referenced by BUFFER.                                                      *@
@* If a binary file transfer is requested then the translation process        *@
@* involves control character quoting and 8th bit prefixing. Otherwise (for   *@
@* text files) the translation process involves control character quoting and *@
@* EBCDIC to ASCII data translation.                                          *@
@* If the whole of BUFFER has been translated then BUFFER_COMPLETE is set     *@
@* TRUE and BUFFER is returned referencing a zero length record. Otherwise    *@
@* BUFFER_COMPLETE is returned FALSE and BUFFER is returned referencing the   *@
@* untranslated part of the original buffer.                                  *@
@* The translated data is returned in the area referenced by DATA.            *@
@* The procedure returns TRUE if there is insufficient space in DATA for more *@
@* of the translated data.                                                    *@
@*                                                                            *@
@******************************************************************************@
 
BEGIN
   STATIC BYTE
      SPACE IS X"20",                                            @ ASCII codes @
      DEL   IS X"7F";
 
   BYTE
      QCTL  IS KMT_PP_LOCAL_CONFG_PARAMS.QCTL,
      QBIN  IS KMT_PP_LOCAL_CONFG_PARAMS.QBIN;
 
   () BYTE
      QLF   IS (QCTL,X"4A"),                               @ ASCII quoted LF   @
      QFF   IS (QCTL,X"4C"),                               @ ASCII quoted FF   @
      QCR   IS (QCTL,X"4D"),                               @ ASCII quoted CR   @
      QCRLF IS (QCTL,X"4D",QCTL,X"4A");                    @ ASCII quoted CRLF @
 
   BOOL
      DATA_COMPLETE := FALSE;
 
   SIM PROC PACKETISE IS (() BYTE SOURCE,INT DATA_LENGTH):
      BEGIN
         IF KMT_PP_MOVE(SOURCE,DATA,DATA_COMPLETE) NE 0
         THEN BUFFER_COMPLETE := KMT_PP_CHECK_COMPLETE(DATA_LENGTH,BUFFER)
         FI
      END @ PACKETISE @ ;
 
   BUFFER_COMPLETE := (LENGTH BUFFER LE 0);
 
   UNTIL DATA_COMPLETE OR BUFFER_COMPLETE
   DO
      IF TEXT_TYPE GT EBCDIC
      THEN                                              @ ASCII or binary data @
         INT SEQ_LEN := 0;
         (3) BYTE SEQ;
         BYTE THIS_CHAR := BUFFER(0);
 
         SIM PROC ADD_TO_SEQ IS (BYTE OCTET):
            BEGIN
               SEQ(SEQ_LEN) := OCTET;
               SEQ_LEN := SEQ_LEN+1
            END @ ADD_TO_SEQ @ ;
 
         IF (THIS_CHAR GT DEL) AND (TEXT_TYPE EQ BINARY)
         THEN                                     @ need to add 8th bit prefix @
            ADD_TO_SEQ(QBIN)
         FI;
 
         THIS_CHAR := THIS_CHAR & X"7F";
 
         IF (THIS_CHAR LT SPACE) OR (THIS_CHAR EQ DEL)
         THEN                                              @ control character @
            ADD_TO_SEQ(QCTL);
            THIS_CHAR := THIS_CHAR NEQ X"40"
         ELSF (THIS_CHAR EQ QCTL) OR (THIS_CHAR EQ QBIN AND TEXT_TYPE EQ BINARY)
         THEN                                         @ special char, quote it @
            ADD_TO_SEQ(QCTL)
         FI;
 
         ADD_TO_SEQ(THIS_CHAR);
         PACKETISE(SEQ(SIZE SEQ_LEN),1)
 
      ELSE                                                       @ EBCDIC data @
         BYTE THIS_CHAR IS EBCDIC_TO_ASCII(BUFFER(0));
 
         IF THIS_CHAR GT DEL
         THEN                                                @ Format effector @
            IF (LENGTH BUFFER LT 2) OR (THIS_CHAR EQ X"FB")
            THEN @ Newline FE (1 byte) or FE at end of record with no specifier@
               IF THIS_CHAR EQ X"FB"
               THEN                                  @ End of record so ignore @
                  BUFFER_COMPLETE := KMT_PP_CHECK_COMPLETE(1,BUFFER)
               ELSE                                                  @ Newline @
                  PACKETISE(QCRLF,1)
               FI
            ELSE                                           @ FE with qualifier @
               REF BYTE QUALIFIER IS BUFFER(1);
 
               SIM PROC EXPAND_FE IS (() BYTE EXPANSION):
                  BEGIN
                     IF
                        QUALIFIER :=
                           QUALIFIER - W'(TO QUALIFIER - 1
                                          UNTIL DATA_COMPLETE
                                          DO
                                             KMT_PP_MOVE(EXPANSION,DATA,
                                                              DATA_COMPLETE)
                                          REPEAT);
                        QUALIFIER EQ 0
                     THEN
                        BUFFER_COMPLETE := KMT_PP_CHECK_COMPLETE(2,BUFFER)
                     FI
                  END @ EXPAND_FE @ ;
 
               CASE X"FF" - THIS_CHAR
               THEN                                           @ multiple space @
                  EXPAND_FE(SPACE)
               ELSE                                         @ multiple newline @
                  IF QUALIFIER EQ X"00"
                  THEN                                       @ translate to CR @
                     PACKETISE(QCR,2)
                  ELSE                                  @ expand to CRLF pairs @
                     EXPAND_FE(QCRLF)
                  FI
               ELSE                                        @ vertical position @
                  IF (QUALIFIER EQ X"00")
                  THEN
                     PACKETISE(QFF,2)
                  ELSE
                     PACKETISE(QCRLF,2)
                  FI
               ELSE                                      @ horizontal position @
                  PACKETISE(SPACE,2)
               ESAC
            FI
         ELSF THIS_CHAR EQ QCTL
         THEN                                                @ quote character @
            PACKETISE((QCTL,THIS_CHAR),1)
         ELSF (THIS_CHAR LT SPACE) OR (THIS_CHAR EQ DEL)
         THEN                                              @ control character @
            PACKETISE((QCTL,THIS_CHAR NEQ X"40"),1)
         ELSE                                            @ printable character @
            PACKETISE(THIS_CHAR,1)
         FI
      FI
   REPEAT;
 
   DATA_COMPLETE
 
END @ KMT_PP_BUILD_PACKET_DATA @ ;
 
***PAGE
 
STATIC
PROC
   KMT_PP_UNQUOTE_CHAR IS (
   REF () BYTE DATA,
   REF BYTE    UNQUOTED_CHAR,
   REF INT     SEQUENCE_LENGTH):
 
@******************************************************************************@
@*                                                                            *@
@* This procedure is used to process a quoted (binary and/or control)         *@
@* character sequence.                                                        *@
@* The area referenced by DATA contains the character sequence to be          *@
@* processed. The area referenced by UNQUOTED_CHAR is returned containing the *@
@* unquoted character (with the control and 8th bit set as appropriate) and   *@
@* the area referenced by SEQUENCE_LENGTH is returned containing the length   *@
@* of the quoted sequence. If the first character in DATA is not a binary or  *@
@* control quote character then UNQUOTED_CHAR is returned containing the      *@
@* character and SEQUENCE_LENGTH is returned containing 1.                    *@
@*                                                                            *@
@******************************************************************************@
 
BEGIN
   WORD TEXT_TYPE IS KMT_FH_RECORD_DETAILS.TEXT_TYPE;
 
   BYTE QCTL IS KMT_PP_REMOTE_CONFG_PARAMS.QCTL,
        QBIN IS KMT_PP_REMOTE_CONFG_PARAMS.QBIN;
 
   BYTE THIS_CHAR := DATA(0);
 
   BYTE OR_MASK IS IF
                      (TEXT_TYPE EQ BINARY)  AND  (THIS_CHAR EQ QBIN)
                   THEN                          @ Binary file and 8th bit     @
                                                 @ quoted character found      @
                      THIS_CHAR := DATA(1);
                      SEQUENCE_LENGTH := 2;
                      X"80"
                   ELSE                          @ Not an 8th bit quoted       @
                                                 @ character                   @
                      SEQUENCE_LENGTH := 1;
                      X"00"
                   FI;
 
   IF
      THIS_CHAR EQ QCTL
 
   THEN                                          @ Control quoted character    @
      THIS_CHAR := DATA(SEQUENCE_LENGTH);
      SEQUENCE_LENGTH := SEQUENCE_LENGTH + 1;
 
      IF
         (THIS_CHAR NE QCTL)  AND  (THIS_CHAR NE QBIN OR TEXT_TYPE NE BINARY)
 
      THEN                                       @ Control character           @
         THIS_CHAR := THIS_CHAR NEQ X"40"
      FI
   FI;
 
   UNQUOTED_CHAR := THIS_CHAR ! OR_MASK
END;                                   @ KMT_PP_UNQUOTE_CHAR                   @
 
***PAGE
 
GLOBAL
STATIC (<STATUS 5>)
PROC
   KMT_PP_TRANSLATE_TO_EBCDIC IS ():
 
@******************************************************************************@
@*                                                                            *@
@* This procedure is used to translate the packet data from ASCII to EBCDIC   *@
@* and to process any quoted characters.                                      *@
@* The untranslated data is read from, and the translated data is returned in *@
@* the area referenced by KMT_PH_INPUT_PACKET_DATA.                           *@
@*                                                                            *@
@******************************************************************************@
 
BEGIN
   () BYTE UNTRANSLATED_DATA := KMT_PH_INPUT_PACKET_DATA;
                                                 @ Copy untranslated data      @
 
   REF () BYTE DATA := UNTRANSLATED_DATA;
 
   BOOL DATA_COMPLETE := (LENGTH DATA LE 0);
 
   INT T_LEN IS FOR   I
                UNTIL
                   DATA_COMPLETE
                DO
                   INT SEQUENCE_LENGTH;
 
                   KMT_PP_UNQUOTE_CHAR (DATA,
                                        KMT_PH_INPUT_PACKET_DATA(I),
                                        SEQUENCE_LENGTH);
                   DATA_COMPLETE := KMT_PP_CHECK_COMPLETE (SEQUENCE_LENGTH,
                                                           DATA)
                REPEAT;
 
   KMT_PH_INPUT_PACKET_DATA := KMT_PH_INPUT_PACKET_DATA(SIZE T_LEN);
   TRANSLATE (ASCII_TO_EBCDIC,
              KMT_PH_INPUT_PACKET_DATA,
              0,
              NIL);
 
   IF KMT_TRACE_FLAGS.PP_TRACING AND LENGTH KMT_PH_INPUT_PACKET_DATA > 0
   THEN (<RARELY>)
     (
      KMT_PP_DATA_TRACE_MESSAGE(KMT_PP_PACKET_DATA_IN_MSG,
                                EBCDIC,
                                KMT_PH_INPUT_PACKET_DATA)
     )
   FI
 
END;                                   @ KMT_PP_TRANSLATE_TO_EBCDIC            @
 
***PAGE
 
GLOBAL
STATIC (<STATUS 5>)
PROC
   KMT_PP_GET_PACKET IS (
   REF INT     TYPE,
   REF INT     SEQ,
   RESPONSE    RESULT):
 
@******************************************************************************@
@*                                                                            *@
@* This procedure is used to receive a KERMIT packet from the remote end.     *@
@* If a failure is detected whilst attempting to read a packet then TYPE is   *@
@* set to BAD_PKT and the failing resultcode is returned.                     *@
@* If a zero length packet is read or no 'MARK' character is found in the     *@
@* packet then TYPE is set to BAD_PKT and resultcode KMT_PP_NO_INPUT_DATA is  *@
@* returned.                                                                  *@
@* If the checksum validation fails then TYPE is set to BAD_PKT and           *@
@* resultcode KMT_PP_INVALID_CHECKSUM is returned.                            *@
@* In all other cases TYPE will be set to either INVALID_PKT or the packet    *@
@* type. In the case of an invalid packet then a resultcode of either UNS_PKT *@
@* or NON_PKT will be returned.                                               *@
@* The packet sequence number is returned in the area referenced by SEQ and   *@
@* the packet data is returned in the area referenced by                      *@
@* KMT_PH_INPUT_PACKET_DATA.                                                  *@
@*                                                                            *@
@******************************************************************************@
 
BEGIN
   INT KMT_PP_PACKET_IN_MSG           IS 251,
       KMT_PP_NO_INPUT_PACKET         IS -80250,
       KMT_PP_INVALID_CHECKSUM        IS -80253,
       KMT_PP_INVALID_PACKET_LENGTH   IS -80254;
 
   INT INPUT_PACKET_LENGTH,
       INPUT_BUFFER_LENGTH,
       PACKET_POINTER_LENGTH;
 
   INT MAX_INPUT_LENGTH IS KMT_DH_DEVICE_DETAILS.MAX_INPUT_LENGTH;
 
   REF () BYTE INPUT_PACKET,
               PACKET_POINTER;
 
   (MAX_INPUT_LENGTH) BYTE INPUT_BUFFER;         @ Allows for padding and      @
                                                 @ 'noise' characters          @
 
   IF
     (
      KMT_DH_INPUT (INPUT_BUFFER,
                    INPUT_BUFFER_LENGTH,
                    RESULT);
      RESULT GT 0
     )
 
   THEN                                          @ I/O error                   @
      TYPE := BAD_PKT
 
   ELSF
      INPUT_BUFFER_LENGTH EQ 0
 
   OR
     (
      BYTE MARK IS KMT_PP_REMOTE_CONFG_PARAMS.MARK;
 
      REF () BYTE REM := INPUT_BUFFER(SIZE INPUT_BUFFER_LENGTH);
 
      PACKET_POINTER := REM(SIZE 0);
 
      UNTIL                                      @ Skip all but last 'MARK'    @
         SCANUNQ (MARK,                          @ character                   @
                  REM,
                  0,
                  REM)
      DO
         PACKET_POINTER := REM;
         REM := REM(1::)
      REPEAT;
 
      PACKET_POINTER_LENGTH := LENGTH PACKET_POINTER;
      PACKET_POINTER_LENGTH LT 2
     )
 
   OR
     (
      INPUT_PACKET_LENGTH := PACKET_POINTER(1) - X"20" + 2;
      PACKET_POINTER_LENGTH LT INPUT_PACKET_LENGTH
     )
 
   THEN                                          @ Either 'MARK' character not @
                                                 @ found or insufficient data  @
                                                 @ after 'MARK' character or a @
                                                 @ recoverable I/O error       @
                                                 @ occurred.                   @
      TYPE := BAD_PKT;
      RESULT := KMT_PP_NO_INPUT_PACKET
 
   ELSF
     (
      (INPUT_PACKET_LENGTH LT 5)  OR  (INPUT_PACKET_LENGTH GT 96)
     )
 
   THEN                                          @ Packet length invalid       @
      TYPE := BAD_PKT;
      RESULT := KMT_PP_INVALID_PACKET_LENGTH
 
   ELSF
     (
      INPUT_PACKET := PACKET_POINTER(SIZE INPUT_PACKET_LENGTH);
 
      IF
         KMT_TRACE_FLAGS.PP_TRACING
 
      THEN (<RARELY>)
        (
         KMT_PP_LOG_TRACE_MESSAGE (KMT_PP_PACKET_IN_MSG,
                                   INPUT_PACKET)
        )
      FI;
 
      INPUT_PACKET(INPUT_PACKET_LENGTH - 1) NE KMT_PP_GENERATE_CHECKSUM (
                                                  INPUT_PACKET)
     )
 
   THEN                                          @ Checksum error              @
      TYPE := BAD_PKT;
      RESULT := KMT_PP_INVALID_CHECKSUM
 
   ELSE                                          @ Validate packet type        @
      WORD PACKET_TYPE IS INPUT_PACKET(3) - X"41";
 
      IF
         PACKET_TYPE GT 25
 
      THEN                                       @ Not in range A to Z         @
         TYPE := INVALID_PKT;
         RESULT := NON_PKT
 
      ELSF
        (
         TYPE := PACKET_CODES(PACKET_TYPE);
         TYPE LT 0
        )
 
      THEN
         RESULT := TYPE;
         TYPE := INVALID_PKT
 
      ELSE
         REF INT DATA_LENGTH IS KMT_PP_INPUT_PACKET_DATA.DATA_LENGTH;
 
         REF () BYTE DATA IS KMT_PP_INPUT_PACKET_DATA.DATA;
 
         REF INT STATISTICS IS
                    KMT_PP_PACKET_STATISTICS.INPUT_TOTAL;
 
         STATISTICS := STATISTICS + 1;
         SEQ := INPUT_PACKET(2) - X"20";
         DATA_LENGTH := INPUT_PACKET_LENGTH - 5;
         MOVE (INPUT_PACKET(4 SIZE DATA_LENGTH),
               DATA,
               0,
               X"00",
               NIL,
               NIL);
         KMT_PH_INPUT_PACKET_DATA := DATA(SIZE DATA_LENGTH)
      FI
   FI
END;                                   @ KMT_PP_GET_PACKET                     @
 
***PAGE
 
GLOBAL
STATIC (<STATUS 5>)
PROC
   KMT_PP_SEND_PACKET IS (
   INT         TYPE,
   INT         SEQ,
   BOOL        PROMPT,
   RESPONSE    RESULT):
 
@******************************************************************************@
@*                                                                            *@
@* This procedure is used to frame and send a KERMIT packet to the remote end.*@
@* TYPE and SEQ specify the packet type and sequence number respectively.     *@
@* The packet data and length is read from the areas                          *@
@* KMT_PP_OUTPUT_PACKET_DATA.DATA and KMT_PP_OUTPUT_PACKET_DATA.DATA_LENGTH   *@
@* respectively.                                                              *@
@* If PROMPT is set TRUE then read interest is set at the PFI for the next    *@
@* input packet.                                                              *@
@*                                                                            *@
@******************************************************************************@
 
BEGIN
   INT KMT_PP_PACKET_OUT_MSG IS 252;
 
   INT DATA_LENGTH IS KMT_PP_OUTPUT_PACKET_DATA.DATA_LENGTH;
 
   REF () BYTE DATA IS KMT_PP_OUTPUT_PACKET_DATA.DATA;
 
   INT OUTPUT_PACKET_LENGTH IS 5 + DATA_LENGTH;
 
   (OUTPUT_PACKET_LENGTH) BYTE OUTPUT_PACKET;
 
   STATIC () BYTE PACKET_TYPES IS X"00"          @ Not used                    @
                                  X"42"          @ B packet                    @
                                  X"44"          @ D packet                    @
                                  X"45"          @ E packet                    @
                                  X"46"          @ F packet                    @
                                  X"47"          @ G packet                    @
                                  X"49"          @ I packet                    @
                                  X"4B"          @ K packet                    @
                                  X"4E"          @ N packet                    @
                                  X"52"          @ R packet                    @
                                  X"53"          @ S packet                    @
                                  X"59"          @ Y packet                    @
                                  X"5A";         @ Z packet                    @
 
   OUTPUT_PACKET(0) := KMT_PP_LOCAL_CONFG_PARAMS.MARK;
   OUTPUT_PACKET(1) := DATA_LENGTH + 3 + X"20";
   OUTPUT_PACKET(2) := SEQ + X"20";
   OUTPUT_PACKET(3) := PACKET_TYPES(TYPE);
   OUTPUT_PACKET(4 SIZE DATA_LENGTH) := DATA;
   OUTPUT_PACKET(OUTPUT_PACKET_LENGTH - 1) := KMT_PP_GENERATE_CHECKSUM (
                                                 OUTPUT_PACKET);
 
   IF
      KMT_TRACE_FLAGS.PP_TRACING
 
   THEN (<RARELY>)
     (
      KMT_PP_LOG_TRACE_MESSAGE (KMT_PP_PACKET_OUT_MSG,
                                OUTPUT_PACKET)
     )
   FI;
 
   KMT_DH_OUTPUT (OUTPUT_PACKET,
                  PROMPT,
                  RESULT);
 
   IF
      RESULT LE 0
 
   THEN
      REF INT STATISTICS IS
                 KMT_PP_PACKET_STATISTICS.OUTPUT_TOTAL;
 
      STATISTICS := STATISTICS + 1
   FI
END;                                   @ KMT_PP_SEND_PACKET                    @
 
***PAGE
 
GLOBAL
STATIC (<STATUS 5>)
PROC
   KMT_PP_BUILD_FILE_PACKET_DATA IS (
   RESPONSE    RESULT):
 
@******************************************************************************@
@*                                                                            *@
@* This procedure is used to read file records and to construct data packets  *@
@* from the record performing data translation, binary and control character  *@
@* character quoting as appropriate.                                          *@
@* The record and length are returned in the data areas                       *@
@* KMT_FH_RECORD_DETAILS.RECORD and KMT_FH_RECORD_DETAILS.RECORD_LENGTH       *@
@* respectively. The processed packet data and length are returned is the     *@
@* data areas                                                                 *@
@* KMT_PP_OUTPUT_PACKET_DATA.DATA and KMT_PP_OUTPUT_PACKET_DATA.DATA_LENGTH   *@
@* respectively.                                                              *@
@* End of file is indicated by returning resultcode DML_READ_PSEUDO_NODE.     *@
@*                                                                            *@
@******************************************************************************@
 
BEGIN
   INT DML_READ_PSEUDO_NODE IS 9034;
 
   REF BOOL END_OF_FILE IS KMT_FH_RECORD_DETAILS.END_OF_FILE;
 
   REF INT DATA_LENGTH IS KMT_PP_OUTPUT_PACKET_DATA.DATA_LENGTH;
 
   RESULT := 0;
 
   IF
      END_OF_FILE
 
   THEN                                          @ End of file reached during  @
                                                 @ previous read               @
      DATA_LENGTH := 0;
      RESULT := DML_READ_PSEUDO_NODE
 
   ELSF
     (
      REF BOOL NEW_RECORD IS KMT_FH_RECORD_DETAILS.NEW_RECORD;
 
      WORD TEXT_TYPE IS KMT_FH_RECORD_DETAILS.TEXT_TYPE;
 
      REF INT RECORD_LENGTH IS KMT_FH_RECORD_DETAILS.RECORD_LENGTH;
 
      REF () BYTE RECORD IS KMT_FH_RECORD_DETAILS.RECORD;
 
      BYTE QCTL IS KMT_PP_LOCAL_CONFG_PARAMS.QCTL;
 
      BYTE MAXL IS KMT_PP_REMOTE_CONFG_PARAMS.MAXL;
 
      REF () BYTE DATA IS KMT_PP_OUTPUT_PACKET_DATA.DATA(SIZE MAXL - 5);
 
      () BYTE QCRQLF IS (QCTL, X"4D", QCTL, X"4A");
                                                 @ Quoted CRLF                 @
      INT D_LEN IS LENGTH DATA;
 
      REF () BYTE DATA_REM := DATA;
 
      (91) BYTE PP_TRACE_BUFFER;                 @ debug trace area            @
      REF () BYTE PP_TRACE_PTR := PP_TRACE_BUFFER;
      INT PP_TRACE_COUNT := 0;
 
      UNTIL
        (
         BOOL DATA_COMPLETE;
 
         IF
            NEW_RECORD
 
         AND
           (
            KMT_FH_READ (RESULT);
 
            IF
               RESULT LE 0
 
            THEN                                 @ Record read successfully    @
               KMT_PP_RECALL_DATA := RECORD(SIZE RECORD_LENGTH);
            FI;
 
            RESULT GT 0
           )
 
         THEN                                    @ Read error or end of file   @
            END_OF_FILE := TRUE;
            DATA_COMPLETE := TRUE
 
         ELSE                                    @ Build packet data from      @
                                                 @ record                      @
            REF () BYTE TRACE_PTR := KMT_PP_RECALL_DATA;
            INT TRACE_CT;
 
            DATA_COMPLETE := KMT_PP_BUILD_PACKET_DATA (KMT_PP_RECALL_DATA,
                                                       DATA_REM,
                                                       TEXT_TYPE,
                                                       NEW_RECORD);
 
                                                 @ copy source to trace buffer @
            TRACE_CT := LENGTH TRACE_PTR - LENGTH KMT_PP_RECALL_DATA;
            PP_TRACE_PTR(SIZE TRACE_CT) := TRACE_PTR(SIZE TRACE_CT);
            PP_TRACE_COUNT := PP_TRACE_COUNT + TRACE_CT;
            PP_TRACE_PTR := PP_TRACE_BUFFER(PP_TRACE_COUNT::);
 
            IF
               TEXT_TYPE EQ BINARY
 
            THEN                                 @ Binary file transfer don't  @
                                                 @ add CRLF end of record      @
                                                 @ marker                      @
               SKIP
 
            ELSF
               NEW_RECORD
 
            THEN                                 @ EBCDIC or ASCII file        @
                                                 @ transfer and end of record. @
                                                 @ Move in quote CRLF, end of  @
                                                 @ record marker               @
               NEW_RECORD := KMT_PP_MOVE (QCRQLF,
                                          DATA_REM,
                                          DATA_COMPLETE) NE 0;
 
               IF NEW_RECORD                    @ add new record mark to trace @
               THEN
                  PP_TRACE_PTR(SIZE 4) := IF TEXT_TYPE = EBCDIC
                                          THEN " // "
                                          ELSE X"202F2F20"
                                          FI;
                  PP_TRACE_COUNT := PP_TRACE_COUNT + 4;
                  PP_TRACE_PTR := PP_TRACE_BUFFER(PP_TRACE_COUNT::);
               FI
            FI
         FI;
 
         DATA_COMPLETE
        )
      DO
         SKIP
      REPEAT;
 
      DATA_LENGTH := D_LEN - LENGTH DATA_REM;
 
      IF KMT_TRACE_FLAGS.PP_TRACING AND PP_TRACE_COUNT > 0
      THEN (<RARELY>)
        (
         IF TEXT_TYPE = ASCII
         THEN
            TRANSLATE(ASCII_TO_EBCDIC,
                      PP_TRACE_BUFFER(SIZE PP_TRACE_COUNT),
                      0,
                      NIL)
         FI;
 
         KMT_PP_DATA_TRACE_MESSAGE(KMT_PP_PACKET_DATA_OUT_MSG,
                                   TEXT_TYPE,
                                   PP_TRACE_BUFFER(SIZE PP_TRACE_COUNT))
        )
      FI;
 
      (RESULT EQ DML_READ_PSEUDO_NODE)  AND  (DATA_LENGTH NE 0)
     )
 
   THEN                                          @ End of file reached but     @
                                                 @ packet data remains.        @
                                                 @ Return end of file on next  @
                                                 @ call                        @
      RESULT := 0
   FI
END;                                   @ KMT_PP_BUILD_FILE_PACKET_DATA         @
 
***PAGE
 
GLOBAL
STATIC (<STATUS 5>)
PROC
   KMT_PP_BUILD_STRING_PACKET_DATA IS (
   REF () BYTE STRING,
   RESPONSE    RESULT):
 
@******************************************************************************@
@*                                                                            *@
@* This procedure is used to translate the data held in STRING from EBCDIC to *@
@* ASCII.                                                                     *@
@* The translated data and length is returned in the area                     *@
@* KMT_PP_OUTPUT_PACKET_DATA.DATA and KMT_PP_OUTPUT_PACKET_DATA.DATA_LENGTH   *@
@* respectively.                                                              *@
@* If the translated data length exceeds the maximum packet data length then  *@
@* resultcode KMT_PP_STRING_TOO_BIG is returned.                              *@
@*                                                                            *@
@******************************************************************************@
 
BEGIN
   INT KMT_PP_STRING_TOO_BIG IS 80256;
 
   INT MAXL IS KMT_PP_REMOTE_CONFG_PARAMS.MAXL;
 
   REF INT DATA_LENGTH IS KMT_PP_OUTPUT_PACKET_DATA.DATA_LENGTH;
 
   REF () BYTE DATA IS KMT_PP_OUTPUT_PACKET_DATA.DATA(SIZE MAXL - 5);
 
   INT STRING_LENGTH IS IF
                           STRING IS NIL
                        THEN
                           0
                        ELSE
                           LENGTH STRING
                        FI;
 
   IF
      STRING_LENGTH EQ 0
 
   THEN                                          @ NIL or ZLR supplied         @
      DATA_LENGTH := 0;
      RESULT := 0
 
   ELSE
      () BYTE STRING_COPY := STRING;             @ Don't want to corrupt       @
                                                 @ original string             @
 
      REF () BYTE STRING_POINTER := STRING_COPY,
                  DATA_REM       := DATA;
 
      RESULT := IF
                  (
                   BOOL STRING_COMPLETE := TRUE;
 
                   KMT_PP_BUILD_PACKET_DATA (STRING_POINTER,
                                             DATA_REM,
                                             EBCDIC,
                                             STRING_COMPLETE);
                   STRING_COMPLETE
                  )
                THEN
                   0
                ELSE                             @ String too big              @
                   KMT_PP_STRING_TOO_BIG
                FI;
 
      DATA_LENGTH := LENGTH DATA - LENGTH DATA_REM;
 
      IF KMT_TRACE_FLAGS.PP_TRACING
      THEN (<RARELY>)
        (
         KMT_PP_DATA_TRACE_MESSAGE(KMT_PP_PACKET_DATA_OUT_MSG,EBCDIC,
                           STRING(SIZE (STRING_LENGTH - LENGTH STRING_POINTER)))
        )
      FI
   FI;
END;                                   @ KMT_PP_BUILD_STRING_PACKET_DATA       @
 
***PAGE
 
GLOBAL
STATIC (<STATUS 5>)
PROC
   KMT_PP_BUILD_FILE_RECORD IS (
   RESPONSE    RESULT):
 
@******************************************************************************@
@*                                                                            *@
@* This procedure is used to construct file records from KERMIT data packets  *@
@* received. For text files control character quoting and data translation    *@
@* from ASCII to EBCDIC is performed. For binary files control character      *@
@* quoting and 8th bit quoting is performed.                                  *@
@* The packet data is read from the area referenced by                        *@
@* KMT_PH_INPUT_PACKET_DATA. The processed record and length is returned in   *@
@* the areas KMT_FH_RECORD_DETAILS.RECORD and                                 *@
@* KMT_FH_RECORD_DETAILS.RECORD.LENGTH respectively.                          *@
@* When the end of record is detected the record is written to the file.      *@
@*                                                                            *@
@******************************************************************************@
 
BEGIN
 
   INT KMT_PP_RECORD_TOO_BIG IS 80255;
 
   REF () BYTE DATA := KMT_PH_INPUT_PACKET_DATA;
 
   INT DATA_LENGTH IS LENGTH DATA;
 
   REF BOOL NEW_RECORD IS KMT_FH_RECORD_DETAILS.NEW_RECORD;
 
   WORD TEXT_TYPE IS KMT_FH_RECORD_DETAILS.TEXT_TYPE;
 
   INT MAX_RECORD_LENGTH := KMT_FH_RECORD_DETAILS.MAX_RECORD_LENGTH;
 
   REF INT RECORD_LENGTH IS KMT_FH_RECORD_DETAILS.RECORD_LENGTH;
 
   INT RECORD_SIZE IS IF
                          TEXT_TYPE EQ BINARY
                      THEN                       @ Binary file transfer. CRLF  @
                                                 @ end of record terminator    @
                                                 @ not required                @
                          MAX_RECORD_LENGTH
                      ELSE                       @ EBCDIC or ASCII file xfer.  @
                                                 @ Allow for CRLF pair, end of @
                                                 @ record terminator           @
                          MAX_RECORD_LENGTH + 2
                      FI;
 
   REF () BYTE RECORD IS KMT_FH_RECORD_DETAILS.RECORD(SIZE RECORD_SIZE);
 
   () BYTE LF IS X"0A",                          @ ASCII LF                    @
           CR IS X"0D";                          @ CR                          @
 
   () BYTE CRLF IS (CR,LF);
 
   BOOL DATA_COMPLETE := (DATA_LENGTH LE 0);
 
   (96) BYTE PP_TRACE_BUFFER;                    @ packet trace variables      @
   REF () BYTE PP_TRACE_PTR := PP_TRACE_BUFFER,
               TRACE_PTR := KMT_PP_RECALL_DATA;
   INT PP_TRACE_COUNT := 0;
 
   RESULT := 0;
 
   UNTIL
      DATA_COMPLETE  OR  (RESULT GT 0)
   DO
      INT LAST_CHAR_INDEX IS IF
                                NEW_RECORD
                             THEN
                                KMT_PP_RECALL_DATA := RECORD;
                                TRACE_PTR := RECORD; @ (for trace)             @
                                -1
                             ELSE
                                RECORD_SIZE - (LENGTH KMT_PP_RECALL_DATA + 1)
                             FI;
 
      BYTE UNQUOTED_CHAR;
 
      INT SEQUENCE_LENGTH;
 
      KMT_PP_UNQUOTE_CHAR (DATA,
                           UNQUOTED_CHAR,
                           SEQUENCE_LENGTH);
 
      IF
         KMT_PP_MOVE (UNQUOTED_CHAR,
                      KMT_PP_RECALL_DATA,
                      NEW_RECORD) NE 0
 
      THEN                                       @ Moved in data               @
         DATA_COMPLETE := KMT_PP_CHECK_COMPLETE (SEQUENCE_LENGTH,
                                                 DATA);
      FI;
 
      IF
         TEXT_TYPE EQ BINARY
 
      THEN                                       @ Binary file transfer        @
         RECORD_LENGTH := RECORD_SIZE - LENGTH KMT_PP_RECALL_DATA;
 
         IF
            NEW_RECORD
 
         THEN                                    @ Record complete, output it  @
                                                 @ (obtain trace info)         @
            INT TRACE_CT := LENGTH TRACE_PTR - LENGTH KMT_PP_RECALL_DATA;
            PP_TRACE_PTR(SIZE TRACE_CT) := TRACE_PTR(SIZE TRACE_CT);
            PP_TRACE_COUNT := PP_TRACE_COUNT + TRACE_CT;
            PP_TRACE_PTR := PP_TRACE_BUFFER(PP_TRACE_COUNT::);
 
            KMT_FH_WRITE (RESULT)                @ output record               @
         FI
 
      ELSF
         (LAST_CHAR_INDEX GE 0)  AND  (RECORD(LAST_CHAR_INDEX SIZE 2) EQ CRLF)
 
      THEN                                       @ EBCDIC or ASCII file xfer   @
                                                 @ end of record, output it    @
                                                 @ (obtain trace info)         @
         INT TRACE_CT := LENGTH TRACE_PTR - (LENGTH KMT_PP_RECALL_DATA + 2);
         IF TRACE_CT = -1                    @ CR LF split between two packets @
         THEN
            TRACE_CT := 0
         ELSE
            PP_TRACE_PTR(SIZE TRACE_CT) := TRACE_PTR(SIZE TRACE_CT);
         FI;
         PP_TRACE_PTR := PP_TRACE_BUFFER((PP_TRACE_COUNT + TRACE_CT)::);
         PP_TRACE_PTR(SIZE 4) := X"202F2F20";
         PP_TRACE_COUNT := PP_TRACE_COUNT + TRACE_CT + 4;
         PP_TRACE_PTR := PP_TRACE_BUFFER(PP_TRACE_COUNT::);
 
         RECORD_LENGTH := RECORD_SIZE - (LENGTH KMT_PP_RECALL_DATA + 2);
                                                 @ Don't want CRLF in file     @
 
         IF
            TEXT_TYPE EQ EBCDIC
 
         THEN                                    @ EBCDIC file transfer        @
            TRANSLATE (ASCII_TO_EBCDIC,
                       RECORD(SIZE RECORD_LENGTH),
                       0,
                       NIL)
         FI;
 
         KMT_FH_WRITE (RESULT);
         NEW_RECORD := TRUE
 
      ELSF
         (
          RECORD_LENGTH := RECORD_SIZE - LENGTH KMT_PP_RECALL_DATA;
          NEW_RECORD
         )
 
      THEN                                       @ EBCDIC or ASCII file xfer   @
                                                 @ and record buffer full but  @
                                                 @ no end of record found.     @
         () KMT_MTM_VALUES PARAMS := DISPLAY(MAX_RECORD_LENGTH
                                             AS KMT_MTM_VALUES.RI_VALUE);
 
         RESULT := KMT_PP_RECORD_TOO_BIG;
         KMT_EH_LOG_ERROR (RESULT,
                           2,
                           PARAMS,
                           0,
                           FALSE,
                           FALSE)
      FI
   REPEAT;
 
   IF KMT_TRACE_FLAGS.PP_TRACING
   THEN (<RARELY>)
     (
      IF DATA_COMPLETE AND NOT NEW_RECORD       @ (obtain rest of trace info) @
      THEN
         INT TRACE_CT := LENGTH TRACE_PTR - LENGTH KMT_PP_RECALL_DATA;
         PP_TRACE_PTR(SIZE TRACE_CT) := TRACE_PTR(SIZE TRACE_CT);
         PP_TRACE_COUNT := PP_TRACE_COUNT + TRACE_CT
      FI;
 
      IF PP_TRACE_COUNT > 0
      THEN
         IF TEXT_TYPE NE BINARY
         THEN
            TRANSLATE(ASCII_TO_EBCDIC,
                      PP_TRACE_BUFFER(SIZE PP_TRACE_COUNT),
                      0,
                      NIL)
         FI;
 
         KMT_PP_DATA_TRACE_MESSAGE(KMT_PP_PACKET_DATA_IN_MSG,
                                   TEXT_TYPE,
                                   PP_TRACE_BUFFER(SIZE PP_TRACE_COUNT))
      FI
     )
   FI
 
END;                                   @ KMT_PP_BUILD_FILE_RECORD              @
 
***PAGE
 
GLOBAL
STATIC (<STATUS 5>)
PROC
   KMT_PP_BUILD_PARAM_PACKET_DATA IS ():
 
@******************************************************************************@
@*                                                                            *@
@* This procedure is used to construct the packet data from the configuration *@
@* parameters.                                                                *@
@* The configuration parameters are read from the area                        *@
@* KMT_PP_LOCAL_CONFG_PARAMS and the packet data and length is returned in    *@
@* the areas KMT_PP_OUTPUT_PACKET_DATA.DATA and                               *@
@* KMT_PP_OUTPUT_PACKET_DATA.DATA_LENGTH respectively.                        *@
@*                                                                            *@
@******************************************************************************@
 
BEGIN
   REF () BYTE DATA IS KMT_PP_OUTPUT_PACKET_DATA.DATA;
 
   BYTE N IS X"4E";                             @ ASCII N                     @
 
   DATA(0) := KMT_PP_LOCAL_CONFG_PARAMS.MAXL + X"20";
   DATA(1) := KMT_PP_LOCAL_CONFG_PARAMS.TIME + X"20";
   DATA(2) := KMT_PP_LOCAL_CONFG_PARAMS.NPAD + X"20";
   DATA(3) := KMT_PP_LOCAL_CONFG_PARAMS.PADC NEQ X"40";
   DATA(4) := KMT_PP_LOCAL_CONFG_PARAMS.EOL + X"20";
   DATA(5) := KMT_PP_LOCAL_CONFG_PARAMS.QCTL;
   DATA(6) := IF
                 KMT_FH_RECORD_DETAILS.TEXT_TYPE NE EBCDIC
              THEN                               @ ASCII or binary file xfer   @
                                                 @ requested, do 8th bit       @
                                                 @ quoting                     @
                 KMT_PP_LOCAL_CONFG_PARAMS.QBIN
              ELSE                               @ EBCDIC file transfer        @
                                                 @ requested, donot do 8th bit @
                                                 @ quoting                     @
                 N
              FI;
                                                 @ We don't support any others @
                                                 @ so default them             @
   KMT_PP_OUTPUT_PACKET_DATA.DATA_LENGTH := 7;
 
   IF KMT_TRACE_FLAGS.PP_TRACING
   THEN (<RARELY>)
     (
      () INT P := (KMT_PP_LOCAL_CONFG_PARAMS.MAXL,
                   KMT_PP_LOCAL_CONFG_PARAMS.TIME,
                   KMT_PP_LOCAL_CONFG_PARAMS.NPAD);
 
      () KMT_MTM_VALUES PARAMS := (P(0)
                                   AS KMT_MTM_VALUES.RI_VALUE,
                                   P(1)
                                   AS KMT_MTM_VALUES.RI_VALUE,
                                   P(2)
                                   AS KMT_MTM_VALUES.RI_VALUE,
                                   KMT_PP_LOCAL_CONFG_PARAMS.PADC
                                   AS KMT_MTM_VALUES.RVB_VALUE,
                                   KMT_PP_LOCAL_CONFG_PARAMS.EOL
                                   AS KMT_MTM_VALUES.RVB_VALUE,
                                   KMT_PP_LOCAL_CONFG_PARAMS.QCTL
                                   AS KMT_MTM_VALUES.RVB_VALUE,
                                   DATA(6)
                                   AS KMT_MTM_VALUES.RVB_VALUE);
 
      KMT_SP_LOG_TRACE_MESSAGE(KMT_PP_PARAM_OUT_MSG,PARAMS)
     )
   FI
 
END;                                   @ KMT_PP_BUILD_PARAM_PACKET_DATA        @
 
***PAGE
 
GLOBAL
STATIC (<STATUS 5>)
PROC
   KMT_PP_PROCESS_PARAM_PACKET_DATA IS (
   RESPONSE RESULT):
 
@******************************************************************************@
@*                                                                            *@
@* This procedure is used to extract the configuration parameters from the    *@
@* packet data.                                                               *@
@* The packet data is read from the area referenced by                        *@
@* KMT_PH_INPUT_PACKET_DATA and the configuration parameters are returned in  *@
@* the area KMT_PP_REMOTE_CONFG_PARAMS.                                       *@
@*                                                                            *@
@******************************************************************************@
 
BEGIN
 
***PAGE
 
SIM
PROC
KMT_PP_LOG_PARAM_ERROR IS (
   INT NAME_CODE,
   REF () BYTE VALUE,
   INT REASON_CODE,
   RESPONSE    RESULT):
 
@******************************************************************************@
@*                                                                            *@
@* This procedure is used to log configuration parameter validation errors to *@
@* the job journal.                                                           *@
@*                                                                            *@
@******************************************************************************@
 
BEGIN
   INT KMT_PP_INVALID_CONFIG_PARAM IS 80257;
   INT NAME := NAME_CODE,
       REASON := REASON_CODE;
 
   () KMT_MTM_VALUES PARAMS := (NAME
                                AS KMT_MTM_VALUES.RI_VALUE,
                                VALUE
                                AS KMT_MTM_VALUES.RVB_VALUE,
                                REASON
                                AS KMT_MTM_VALUES.RI_VALUE);
 
   RESULT := KMT_PP_INVALID_CONFIG_PARAM;
 
   KMT_EH_LOG_ERROR (RESULT,2,PARAMS,0,FALSE,FALSE)
 
END;                                   @ KMT_PP_LOG_PARAM_ERROR                @
 
***PAGE
 
   INT KMT_PP_QBIN_AND_QCTL_EQUAL IS 80258,
       KMT_PP_QCTL_WARNING        IS -80259,
       KMT_PP_QBIN_REJECTED       IS 80260;
 
   INT MAXL_CODE IS 0,
       NPAD_CODE IS 1,
       PADC_CODE IS 2,
       EOL_CODE  IS 3,
       QCTL_CODE IS 4,
       QBIN_CODE IS 5;
 
   INT INFO_LENGTH IS SIZEOF KMT_PP_CONFG_PARAMS_S;
 
   (INFO_LENGTH) BYTE PACKET_INFO;
 
   WORD MAX_OUTPUT_LENGTH IS KMT_DH_DEVICE_DETAILS.MAX_OUTPUT_LENGTH;
 
   WORD TEXT_TYPE IS KMT_FH_RECORD_DETAILS.TEXT_TYPE;
 
   WORD MIN_PACKET_LENGTH IS 30;
 
   INT DATA_LENGTH IS LENGTH KMT_PH_INPUT_PACKET_DATA;
 
   REF BYTE MAXL IS KMT_PP_REMOTE_CONFG_PARAMS.MAXL,
            TIME IS KMT_PP_REMOTE_CONFG_PARAMS.TIME,
            NPAD IS KMT_PP_REMOTE_CONFG_PARAMS.NPAD,
            PADC IS KMT_PP_REMOTE_CONFG_PARAMS.PADC,
            EOL  IS KMT_PP_REMOTE_CONFG_PARAMS.EOL,
            QCTL IS KMT_PP_REMOTE_CONFG_PARAMS.QCTL,
            QBIN IS KMT_PP_REMOTE_CONFG_PARAMS.QBIN,
            CHKT IS KMT_PP_REMOTE_CONFG_PARAMS.CHKT,
            REPT IS KMT_PP_REMOTE_CONFG_PARAMS.REPT;
 
   REF 4-BYTE CAPAS IS KMT_PP_REMOTE_CONFG_PARAMS.CAPAS;
 
   REF BYTE LOCAL_MARK IS KMT_PP_LOCAL_CONFG_PARAMS.MARK,
            LOCAL_QCTL IS KMT_PP_LOCAL_CONFG_PARAMS.QCTL,
            LOCAL_QBIN IS KMT_PP_LOCAL_CONFG_PARAMS.QBIN;
 
   INT RC;
 
   BYTE PARAM;
 
   BYTE NUL    IS X"00",                         @ ASCII NUL                   @
        CR     IS X"0D",                         @ CR                          @
        SPACE  IS X"20",                         @ Space                       @
        HASH   IS X"23",                         @ #                           @
        ONE    IS X"31",                         @ 1                           @
        RAB    IS X"3E",                         @ >                           @
        CTLNUL IS X"40",                         @ Controlled NUL              @
        N      IS X"4E",                         @ N                           @
        Y      IS X"59",                         @ Y                           @
        GRAVE  IS X"60",                         @ `                           @
        TILDE  IS X"7E",                         @ ~                           @
        DEL    IS X"7F";                         @ DEL                         @
 
   RESULT := 0;
 
   MOVEBYTE (SPACE,                              @ Set defaults                @
             PACKET_INFO,
             0,
             NIL);
   PACKET_INFO(3) := CTLNUL;                     @ PADC default                @
 
   PACKET_INFO(SIZE DATA_LENGTH) := KMT_PH_INPUT_PACKET_DATA;
 
   IF KMT_TRACE_FLAGS.PP_TRACING
   THEN (<RARELY>)
     (
      () INT IP := (PACKET_INFO(0) - X"20",
                    PACKET_INFO(1) - X"20",
                    PACKET_INFO(2) - X"20",
                    PACKET_INFO(7) - X"30");
 
      () BYTE BP := (PACKET_INFO(3) NEQ X"40",
                     PACKET_INFO(4) - X"20");
 
      () KMT_MTM_VALUES PARAMS := (IP(0)                               @ MAXL  @
                                   AS KMT_MTM_VALUES.RI_VALUE,
                                   IP(1)                               @ TIME  @
                                   AS KMT_MTM_VALUES.RI_VALUE,
                                   IP(2)                               @ NPAD  @
                                   AS KMT_MTM_VALUES.RI_VALUE,
                                   BP(0)                               @ PADC  @
                                   AS KMT_MTM_VALUES.RVB_VALUE,
                                   BP(1)                               @ EOL   @
                                   AS KMT_MTM_VALUES.RVB_VALUE,
                                   PACKET_INFO(5)                      @ QBIN  @
                                   AS KMT_MTM_VALUES.RVB_VALUE,
                                   PACKET_INFO(6)                      @ QCTL  @
                                   AS KMT_MTM_VALUES.RVB_VALUE,
                                   IP(3)                               @ CHKT  @
                                   AS KMT_MTM_VALUES.RI_VALUE,
                                   PACKET_INFO(8)                      @ REPT  @
                                   AS KMT_MTM_VALUES.RVB_VALUE,
                                   PACKET_INFO(9 SIZE 4)               @ CAPAS @
                                   AS KMT_MTM_VALUES.RVB_VALUE);
 
      KMT_SP_LOG_TRACE_MESSAGE(KMT_PP_PARAM_IN_MSG,PARAMS(SIZE DATA_LENGTH))
     )
   FI;
 
   PARAM := PACKET_INFO(0) - X"20";
   MAXL := IF
              (PARAM EQ 0)
           OR
              (PARAM GE MAX_OUTPUT_LENGTH)
           THEN                                  @ Defaulted or greater than   @
                                                 @ device line length          @
               MAX_OUTPUT_LENGTH
           ELSE
               PARAM
           FI;
 
   TIME := PACKET_INFO(1) - X"20";
   NPAD := PACKET_INFO(2) - X"20";
   PADC := PACKET_INFO(3) NEQ X"40";
   PARAM := PACKET_INFO(4) - X"20";
   EOL := IF
             PARAM EQ 0
          THEN                                   @ defaulted, set to CR        @
             CR
          ELSE
             PARAM
          FI;
 
   PARAM := PACKET_INFO(5);
   QCTL := IF
              PARAM EQ SPACE
           THEN                                  @ Defaulted, set to #         @
               HASH
           ELSE
               PARAM
           FI;
 
   PARAM := PACKET_INFO(6);
   QBIN := IF
              PARAM EQ N                         @ ASCII N                     @
           THEN                                  @ 8th bit quoting rejected    @
              SPACE
           ELSF
              PARAM EQ Y                         @ ASCII Y                     @
           THEN                                  @ 8th bit quoting requested   @
              LOCAL_QBIN
           ELSE
              PARAM
           FI;
                                                 @ We donot support any others @
                                                 @ so default them             @
   CHKT  := ONE;
   REPT  := NUL;
   CAPAS := NUL;
                                                 @ Validate configuration      @
                                                 @ parameters                  @
   IF MAXL LT MIN_PACKET_LENGTH
   THEN                                          @ Invalid MAXL parameter      @
      KMT_PP_LOG_PARAM_ERROR (MAXL_CODE,MAXL,80270,RESULT)
   FI;
 
   IF (MAXL + NPAD) LE MAX_OUTPUT_LENGTH
   THEN
      SKIP
   ELSF                                          @ Device line length exceeded @
     (                                           @ Adjust MAXL                 @
      MAXL := MAX_OUTPUT_LENGTH - NPAD;
      MAXL LT MIN_PACKET_LENGTH
     )
   THEN                                          @ NPAD parameter invalid      @
      KMT_PP_LOG_PARAM_ERROR (NPAD_CODE,NPAD,80271,RESULT)
   FI;
 
   IF (PADC GT SPACE)  AND  (PADC NE DEL)
   THEN                                          @ Invalid PADC character      @
      KMT_PP_LOG_PARAM_ERROR (PADC_CODE,PADC,80272,RESULT)
   FI;
 
   IF EOL = LOCAL_MARK
   THEN                                          @ EOL conflicts with MARK     @
      KMT_PP_LOG_PARAM_ERROR (EOL_CODE,EOL,80276,RESULT)
   ELSF KMT_SP_CHECK_VME_CHAR(EOL,1,RC);
        RC NE 0
   THEN                                          @ EOL not acceptable to VME   @
      KMT_PP_LOG_PARAM_ERROR (EOL_CODE,EOL,80274,RESULT)
   FI;
 
   IF NPAD > 0
   THEN                                          @ check for PADC conflict     @
      IF PADC = LOCAL_MARK
      THEN                                       @ PADC conflicts with MARK    @
         KMT_PP_LOG_PARAM_ERROR (PADC_CODE,PADC,80276,RESULT)
      ELSF PADC = EOL
      THEN                                       @ PADC conflicts with EOL     @
         KMT_PP_LOG_PARAM_ERROR (PADC_CODE,PADC,80277,RESULT)
      ELSF KMT_SP_CHECK_VME_CHAR(PADC,1,RC);
           RC NE 0
      THEN                                      @ PADC not acceptable to VME   @
         KMT_PP_LOG_PARAM_ERROR (PADC_CODE,PADC,80274,RESULT)
      FI
   FI;
 
   IF (QCTL LE SPACE)  OR  (QCTL GE DEL)
   THEN                                          @ Invalid QCTL character      @
      KMT_PP_LOG_PARAM_ERROR (QCTL_CODE,QCTL,80273,RESULT)
   FI;
 
   IF (QCTL GT RAB)  AND  (QCTL LT GRAVE)
   THEN
      @ KERMIT protocol permits the QCTL character to be any printable         @
      @ character. However a QCTL character value in the range X3F to X5F may  @
      @ result in data corruption. A warning message is logged but the choice  @
      @ of QCTL is permitted as the protocol allows it. eg Consider a QCTL     @
      @ character of '?' (X3F) and data containing the character DEL (X7F).    @
      @ The sender encodes this as a two byte sequence: QCTL (DEL XOR X40)     @
      @ ie '??' (X3F3F). The quoting rule states that QCTL caharacters         @
      @ appearing in the data must also be prefixed. Therefore the receiver    @
      @ will decode the sequence as '?' (X3F) not DEL (X7F) as required.       @
 
      BYTE CORRUPTED_CHAR := (QCTL NEQ X"40");
 
      () KMT_MTM_VALUES PARAMS := (QCTL
                                   AS KMT_MTM_VALUES.RVB_VALUE,
                                   CORRUPTED_CHAR
                                   AS KMT_MTM_VALUES.RVB_VALUE);
 
      KMT_EH_LOG_ERROR (KMT_PP_QCTL_WARNING,2,PARAMS,0,FALSE,FALSE)
   FI;
 
   IF                                                         TEXT_TYPE EQ EBCDIC
   THEN                                          @ EBCDIC file transfer        @
      SKIP
   ELSF
         (QBIN LT SPACE)
      OR
         (QBIN GT TILDE)
      OR
         ((QBIN GT RAB)  AND  (QBIN LT GRAVE))
 
   THEN                                          @ Invalid QBIN character      @
      KMT_PP_LOG_PARAM_ERROR (QBIN_CODE,QBIN,80275,RESULT)
   ELSF
      QBIN EQ SPACE
   THEN                                          @ QBIN character rejected     @
      RESULT := KMT_PP_QBIN_REJECTED;
      KMT_EH_LOG_ERROR (RESULT,2,NIL,0,FALSE,FALSE)
   ELSF
      QBIN EQ QCTL  OR  QBIN EQ LOCAL_QCTL
   THEN                                          @ ASCII or binary file xfer,  @
                                                 @ but QBIN and QCTL characters@
                                                 @ are equal                   @
      () KMT_MTM_VALUES PARAMS := (QBIN
                                   AS KMT_MTM_VALUES.RVB_VALUE,
                                   QCTL
                                   AS KMT_MTM_VALUES.RVB_VALUE,
                                   LOCAL_QCTL
                                   AS KMT_MTM_VALUES.RVB_VALUE);
 
      RESULT := KMT_PP_QBIN_AND_QCTL_EQUAL;
      KMT_EH_LOG_ERROR (RESULT,2,PARAMS,0,FALSE,FALSE)
   ELSE                                          @ IA5 or binary file transfer,@
                                                 @ and QCTL and QBIN characters@
                                                 @ are acceptable              @
      LOCAL_QBIN := QBIN
   FI
END;                                   @ KMT_PP_PROCESS_PARAM_PACKET_DATA      @
ENDMODULE                              @ KMT_PP_MODULE                         @
