MODULE iim$send_output_message;
*copyc osd$default_pragmats

  TYPE
    iit$mli_status = set of mlt$status;

?? PUSH (LISTEXT := ON) ??
*copyc ifv$module_for_c180
*copyc iik$keypoints
*copyc iit$application_names_messages
*copyc iit$connection_description
*copyc iiv$interactive_terminated
*copyc iiv$int_task_open_file_count
*copyc osp$set_job_signature_lock
*copyc osp$set_status_abnormal
*copyc osp$test_sig_lock
*copyc osp$clear_job_signature_lock
*copyc osp$establish_condition_handler
*copyc osp$disestablish_cond_handler
*copyc pmp$continue_to_cause
*copyc ife$error_codes
*copyc i#move
*copyc iip$delete_queue_entry
*copyc iip$add_queue_entry
*copyc iip$report_status_error
*copyc iip$free_queue_entry
*copyc iip$build_data_msg_skeleton
*copyc iip$convert_downline_block
*copyc iip$send_attributes_change
*copyc mld$memory_link_declarations
*copyc mlp$send_message
*copyc pmp$long_term_wait
*copyc tmc$wait_times
*copyc pmp$task_state
*copyc osp$system_error
*copyc iiv$output
*copyc i#current_sequence_position
*copyc iip$check_if_status
?? POP ??

  PROCEDURE [XDCL] iip$send_output_message (wait: boolean;
    VAR status: ost$status);

    VAR
      dqe: ^iit$downline_queue_entry,
      nfe: iit$no_format_effectors,
      xpt: iit$transparent_mode,
      tcc: boolean,
      tcs: boolean,
      tcn: boolean,
      cdp: ^iit$connection_description,
      block_type: iit$application_block_type,
      cml: 0..0ffffffffffff(16),
      lib: integer,
      len: 0..0ffffffffffff(16),
      xpt_length: 0 .. 1,
      output_data: iit$output_data_message,
      c170_message_length: mlt$message_length,
      rqed,
      qed: iit$queue_entry_descriptor,
      tlength,
      i: integer,
      ls: ost$signature_lock_status,
      signal_response: integer,
      iiv$begin_absentee: [XREF] boolean,
      first_wait: boolean,
      first_po,
      po: ^iit$output,
      p_length: ^integer,
      iiv$job_output: [XDCL] ^SEQ ( * ) := NIL,
      iiv$last_output_time: [XDCL] integer := 0,
      ost: ost$status,
      output_data_message: iit$output_data_message;

?? NEWTITLE := 'PROCEDURE handle_break', EJECT ??

    PROCEDURE handle_break (cond: pmt$condition;
          cd: ^pmt$condition_information;
          sa: ^ost$stack_frame_save_area;
      VAR ch_status: ost$status);

      VAR
        local_status: ost$status;

      osp$test_sig_lock (iiv$downline_queue_lock, ls);
      IF ls = osc$sls_locked_by_current_task THEN
        osp$clear_job_signature_lock (iiv$downline_queue_lock);
      IFEND;

      pmp$continue_to_cause (pmc$execute_standard_procedure, local_status);

      IF cond.selector = ifc$interactive_condition THEN
        CASE cond.interactive_condition OF
        = ifc$pause_break =
          osp$set_status_abnormal (ifc$interactive_facility_id,
                ife$pause_break_received, '', status);
        = ifc$terminate_break =
          osp$set_status_abnormal (ifc$interactive_facility_id,
                ife$terminate_break_received, '', status);
        = ifc$terminal_connection_broken =
          osp$set_status_abnormal (ifc$interactive_facility_id,
                ife$connection_break_disconnect, '', status);
        = ifc$job_reconnect =
          osp$set_status_abnormal (ifc$interactive_facility_id,
                ife$terminal_reconnected_to_job, '', status);
        ELSE
          osp$set_status_abnormal (ifc$interactive_facility_id, 0,
            'unknown interactive condition encountered', status);
        CASEND;

        osp$test_sig_lock (iiv$downline_queue_lock, ls);
        IF ls = osc$sls_locked_by_current_task THEN
          osp$clear_job_signature_lock (iiv$downline_queue_lock);
        IFEND;
        EXIT iip$send_output_message;

      IFEND;

      ch_status.normal := TRUE;

    PROCEND handle_break;
?? OLDTITLE ??
?? EJECT ??
    status.normal := TRUE;
    #KEYPOINT (osk$entry, 0, iik$send_output_message);

{   interlock the send output message operation (1 per job)

    osp$test_sig_lock (iiv$downline_queue_lock, ls);
    IF ls = osc$sls_locked_by_current_task THEN
      RETURN;
    IFEND;
    osp$establish_condition_handler (^handle_break, TRUE);
    osp$set_job_signature_lock (iiv$downline_queue_lock);

  /send_output_message/
    WHILE TRUE DO

{     discard downline stuff ...

      IF iiv$interactive_terminated OR iiv$job_suspended THEN
        RESET iiv$output;
        RESET iiv$job_output;
        iiv$downline_queue_count := 0;
        EXIT /send_output_message/;
      IFEND;


{     if queue is empty then exit

      IF iiv$downline_queue_count = 0 THEN
        EXIT /send_output_message/;
      IFEND;

{     data is present - try to pack as many downline queue entries into
{     one output message as possible.  the following conditions terminate
{     the building of a message:
{     - downline queue empty
{     - output message full
{  - change in block modes (transparent, format effectors)
{     - terminal attributes change

      cml := 0;
      tlength := 0;
      NEXT p_length IN iiv$job_output;
      RESET iiv$job_output TO p_length;
      NEXT po: [1 .. p_length^] IN iiv$job_output;
      first_po := po;
      dqe := ^po^.block;
      nfe := dqe^.message.header.no_format_effectors;
      xpt := dqe^.message.header.transparent;
      tcc := dqe^.term_char_changed;
      tcs := dqe^.term_char_sent;
      tcn := dqe^.term_char_null;
      cdp := dqe^.connection_ptr;

{     Send terminal characteristics pairs if NAM terminal attributes
{     have been changed and they have not been sent yet.

      IF tcc AND NOT tcs THEN
        iip$send_attributes_change (dqe, cdp, status);
        IF status.normal THEN
          dqe^.term_char_sent := TRUE;
        IFEND;
        RESET iiv$job_output TO po;
        CYCLE /send_output_message/;
      IFEND;

      IF xpt THEN
        xpt_length := 0;
      ELSE
        xpt_length := 1;
      IFEND;

    /build_output_message/
      WHILE TRUE DO
        len := dqe^.message.header.text_length;
        tcn := dqe^.term_char_null;

{       check if to terminate building the current message

        IF (nfe <> dqe^.message.header.no_format_effectors) OR (xpt <> dqe^.
              message.header.transparent)
              OR (cml + len + xpt_length > (iic$max_block_size -
              8)) OR ((cml > 0) AND dqe^.term_char_changed) THEN
          RESET iiv$job_output TO po;
          EXIT /build_output_message/;
        IFEND;
        block_type := dqe^.message.header.block_type;

{       add this queue entry to the message being built

        IF (NOT tcn) OR (len > 0) THEN
          i#move (#LOC (dqe^.message.data [1]), #LOC (output_data.data [cml +
                1]), len);
          cml := cml + len;

{         add unit separator if not transparent

          IF NOT xpt THEN
            IF len = 0 THEN
              output_data.data [cml + 1] := ' ';
              cml := cml + 1;
            IFEND;
            output_data.data [cml + 1] := iic$ascii_us;
            cml := cml + 1;
          IFEND;
        IFEND;
        tlength := tlength + p_length^;


{       terminate building block if the last message added was a msg block

        IF block_type = iic$last_block THEN
          EXIT /build_output_message/;
        IFEND;

{       attempt to continue if there are any more queue entries left
{       set dqe to point to the next entry to add to the message

        IF tlength < iiv$downline_queue_count THEN
          NEXT p_length IN iiv$job_output;
          RESET iiv$job_output TO p_length;
          NEXT po: [1 .. p_length^] IN iiv$job_output;
          dqe := ^po^.block;
        ELSE
          EXIT /build_output_message/;
        IFEND;
      WHILEND /build_output_message/;

{     Send a downline block if this is not a terminal characteristics
{     null block.

      iiv$downline_queue_count := iiv$downline_queue_count - tlength;
      IF (cml > 0) OR (block_type = iic$last_block) THEN

{       Build nam output_data_message and send it to passon.

        iip$build_data_msg_skeleton (^output_data, cml);
        output_data.header.no_format_effectors := nfe;
        output_data.header.transparent := xpt;

{       set acn here from job acn to insure it is correct for reconnected job.

        output_data.header.connection_number := iiv$job_connection;
        signal_response := 0;
        output_data.header.block_type := block_type;
        IF (block_type = iic$last_block) THEN
           signal_response := iic$dont_signal;
           IF (iiv$begin_absentee) THEN
           output_data.header.block_type := iic$begin_absentee;
           IFEND;
        IFEND;
        output_data.header.zero1 := 0;

        iip$convert_downline_block (#LOC (output_data), #LOC
              (output_data_message), cml + #SIZE (output_data_message.header),
              c170_message_length);

        first_wait := TRUE;
        REPEAT

          mlp$send_message (iiv$int_application_name, iic$output_data_message +
                signal_response + iiv$job_connection, NIL, #LOC (output_data_message),
                c170_message_length, iic$passon_application_name, status);

          IF NOT status.normal THEN
            IF NOT wait THEN
              {Re-queue output
              RESET iiv$job_output TO first_po;
              iiv$downline_queue_count := iiv$downline_queue_count + tlength;
              status.normal := TRUE;
              EXIT /send_output_message/;
            IFEND;
            {Note: wait is done with lock set - depends on iip$check_if_status below.
            IF status.condition = mlc$prior_msg_not_received THEN
              IF first_wait THEN { inhibit swap out of this job }
                pmp$long_term_wait (250, 250);
                first_wait := FALSE;
              ELSE
                {allow swap out of this job }
                pmp$long_term_wait (tmc$infinite_wait, tmc$infinite_wait);
              IFEND;
            ELSE
              pmp$long_term_wait (250, 250);
            IFEND;
            {Detect asynchronous interrupts
            iip$check_if_status (ost);
            IF NOT ost.normal THEN
              status := ost;
              EXIT /send_output_message/;
            IFEND;

            IF pmp$task_state () = pmc$task_terminating THEN
              EXIT /send_output_message/;
            IFEND;
          IFEND;

        UNTIL status.normal;

      ELSE

        status.normal := TRUE;

      IFEND;

      iiv$last_output_time := #free_running_clock (0);
      IF iiv$downline_queue_count <= 0 THEN
        IF (iiv$downline_queue_count < 0) THEN
          osp$system_error ('SOM CONFUSED', NIL);
        IFEND;
        RESET iiv$output;
        RESET iiv$job_output;
      IFEND;

      {If send output successful - exit now.
      EXIT /send_output_message/;

    WHILEND /send_output_message/;

    osp$clear_job_signature_lock (iiv$downline_queue_lock);
    osp$disestablish_cond_handler;

    #KEYPOINT (osk$exit, 0, iik$send_output_message);

  PROCEND iip$send_output_message;
MODEND iim$send_output_message
