?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Queue File Transfer Facility Controller' ??
MODULE nfm$qtf_controller;

{ PURPOSE:
{   QTF gives a user the capability to send input, output, and generic queue
{   files to remote mainframes connected by CDCNET or LCN.  This module, QTF
{   Controller (QTFC), makes up half the QTF client.  The other half is the QTF
{   Initiator (QTFI).  QTFC gets files from Queue File Manager (QFM), starts the
{   necessary QTFI tasks to transmit the files to the remote mainframe.
{
{ DESIGN:
{   QTFC acquires all input, output, and generic queue files that are to be
{   transferred to remote destinations from QFM.  Additionally, QTFC acquires
{   all queue files that have been terminated or modified by the controlling
{   user.  The priority of acquiring queue files is:  terminated, modified, and
{   new.
{
{   After acquiring the queue files, QTFC will check to see if there are any
{   files to be transferred to remote destinations and determines whether the
{   remote destinations are available through LCN or CDCNET.  If a destination
{   is available via LCN, an RHFAM connection is established.  If the
{   destination is only available via CDCNET, a NAM/VE connection is made.
{
{   If a remote destination is not available through either network QTFC will do
{   two things:  Issue a title translation request with NAM/VE, and periodically
{   check the destination's availability via RHFAM through the use of back-off
{   timers.  If the NAM/VE translation request is satisfied before the
{   destination becomes available via RHFAM, QTFC will check if the destination
{   can be reached via RHFAM once more.  This extra RHFAM check is done because
{   RHFAM / LCN is the preffered network media.  If the remote destination is
{   found via RHFAM before the NAM/VE translation request is satisified, the
{   RHFAM connection is established and the NAM/VE translation request is
{   removed.
{
{   After establishing a connection to the remote destination, QTFC will start
{   an asynchronous QTFI task and provide the task with the connection and the
{   queue file to transfer.  After attempting to transfer the queue file to the
{   remote destination, QTFI will send a message to QTFC describing the status
{   of the transfer.
{
{   If the file transfer completed normally QTFC will provide QTFI another file
{   to transfer over the same connection.  If the file transfer completed
{   abnormally QTFC will attempt to retry the file again at a later time based
{   and the type of failure encountered during the file transfer.
{
{   If there are no queue files remaining to transfer to a particular
{   destination, or the limit of transferred files has been reached, or the
{   previous file transfer failed miserably, QTFC will tell QTFI to close the
{   current connection and provide a new connection for future file transfers.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc jmt$qfile_attribute_keys
*copyc nae$application_interfaces
*copyc nfc$qtf_name_constants
*copyc nfc$timer_values
*copyc nfe$batch_transfer_services
*copyc nft$intertask_message
*copyc nft$network_type
*copyc nft$wait_list_size
*copyc osd$integer_limits
*copyc ost$status
*copyc osv$lower_to_upper
?? POP ??
*copyc amp$return
*copyc clp$delete_variable
*copyc clp$evaluate_parameters
*copyc clp$trimmed_string_size
*copyc jmp$acquire_modified_input
*copyc jmp$acquire_modified_output
*copyc jmp$acquire_modified_qfile
*copyc jmp$acquire_new_input
*copyc jmp$acquire_new_output
*copyc jmp$acquire_new_qfile
*copyc jmp$get_result_size
*copyc jmp$modified_input_exists
*copyc jmp$modified_output_exists
*copyc jmp$modified_qfile_exists
*copyc jmp$new_input_exists
*copyc jmp$new_output_exists
*copyc jmp$new_qfile_exists
*copyc jmp$register_input_application
*copyc jmp$register_output_application
*copyc jmp$register_qfile_application
*copyc jmp$set_input_completed
*copyc jmp$set_input_initiated
*copyc jmp$set_output_completed
*copyc jmp$set_output_initiated
*copyc jmp$set_qfile_completed
*copyc jmp$set_qfile_initiated
*copyc jmp$terminate_acquired_input
*copyc jmp$terminate_acquired_output
*copyc jmp$terminate_acquired_qfile
*copyc jmp$terminated_input_exists
*copyc jmp$terminated_output_exists
*copyc jmp$terminated_qfile_exists
*copyc nap$begin_directory_search
*copyc nap$display_message
*copyc nap$end_directory_search
*copyc nap$get_title_translation
*copyc nap$request_connection
*copyc nfp$close_store_forward_file
*copyc nfp$create_appl_def_segment_var
*copyc nfp$end_async_communication
*copyc nfp$get_async_task_message
*copyc nfp$get_new_destination_name
*copyc nfp$get_new_source_name
*copyc nfp$open_store_forward_file
*copyc nfp$put_async_task_message
*copyc nfp$request_asynchronous_task
*copyc nfp$start_timer
*copyc nfp$timer_expired
*copyc osp$i_await_activity_completion
*copyc osp$set_status_abnormal
*copyc pmp$establish_condition_handler
*copyc pmp$get_compact_date_time
*copyc pmd$local_queues
*copyc pmp$get_unique_name
*copyc pmp$log
*copyc pmp$wait
*copyc rfp$application_sign_on
*copyc rfp$application_sign_off
*copyc rfp$await_server_response
*copyc rfp$find_available_service
*copyc rfp$get_local_host_physical_id
*copyc rfp$request_connection
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

  CONST
    nfc$qtf_put_async_max_retry = 10,
    nfc$qtf_put_async_wait_time = 250,        {  this is in milliseconds.  (one-fourth of a second)
    nfc$wait_list_limit = 20;

  TYPE
    nft$bcd_digit = 0 .. 9,

    nft$bcd_time = packed record
      date: packed record
        year1: nft$bcd_digit,
        year2: nft$bcd_digit,
        month1: nft$bcd_digit,
        month2: nft$bcd_digit,
        day1: nft$bcd_digit,
        day2: nft$bcd_digit,
      recend,
      time: packed record
        hours1: nft$bcd_digit,
        hours2: nft$bcd_digit,
        minutes1: nft$bcd_digit,
        minutes2: nft$bcd_digit,
        seconds1: nft$bcd_digit,
        seconds2: nft$bcd_digit,
        milliseconds1: nft$bcd_digit,
        milliseconds2: nft$bcd_digit,
        milliseconds3: nft$bcd_digit,
        fill: nft$bcd_digit,
      recend,
    recend,

*copyc nft$micro_second
*copyc nft$timer
    nft$network_availability = record
      case available: boolean of
      = FALSE =
        timer: nft$timer,
      = TRUE =
        time_stamp: nft$bcd_time,
      casend,
    recend,

    nft$qtf_connection = record
      file_name: amt$local_file_name,
      kind: nft$network_type,
    recend,

    nft$qtf_put_async_retry_range = 0 .. (nfc$qtf_put_async_max_retry + 1),

    nft$qtfc_destination = record
      back_link: ^nft$qtfc_destination,
      link: ^nft$qtfc_destination,
      name: ost$name, {family name or LID
      next_destination_name: nft$parameter_24_definition,
      file_list: ^nft$qtfc_file,
      last_file: ^nft$qtfc_file,
      retry_timer: nft$timer,
      rhfam_timer: nft$timer,
      transfering_task: ^nft$qtfi_task,
      translation_request: ^ost$i_activity,
    recend,

    nft$qtfc_file = record
      back_link: ^nft$qtfc_file,
      link: ^nft$qtfc_file,
      name: jmt$system_supplied_name,
      transfer_state: nft$qtfc_file_transfer_state,
      application_file_descriptor: nft$application_file_descriptor,
    recend,

    nft$qtfc_file_transfer_state = (nfc$wait_to_transfer, nfc$ready_to_transfer, nfc$transfer_initiated),

    nft$qtfi_task = record
      back_link: ^nft$qtfi_task,
      link: ^nft$qtfi_task,
      file_in_transfer: ^nft$qtfc_file,
      file_transfer_start_time: nft$micro_second,
      last_message_sent: nft$intertask_message_kind,
      qid: pmt$queue_connection,
      task_id: pmt$task_id,
      transfer_connection: nft$qtf_connection,
    recend;

  VAR
    destination_list: ^nft$qtfc_destination := NIL,
    qtfc_wait_list_seq: ^SEQ ( * ) := NIL;

*copyc nfs$appl_def_segment_variables
*copyc nfv$appl_def_segment_variables
?? TITLE := 'acquire_all_q_files', EJECT ??

{ PURPOSE:
{   This procedure gets all available queue files from the Queue File
{   Manager to be transferred via QTF.  The files will be deleted from
{   file lists (terminated files), modified and/or moved to different
{   file lists (modified files), and added to file lists (new files).

  PROCEDURE acquire_all_q_files
    (    generic_q_password: jmt$queue_file_password;
         input_q_password: jmt$queue_file_password;
         output_q_password: jmt$queue_file_password;
         store_forward_file_info: nft$store_forward_file_info;
     VAR destination_list: ^nft$qtfc_destination);

?? NEWTITLE := 'acquire_terminated_files', EJECT ??

{ PURPOSE:
{   This procedure gets all the terminated output, input and generic queue files
{   from QFM and removes the files from the internal tables.

    PROCEDURE acquire_terminated_files
      (    destination_list: ^nft$qtfc_destination);

      VAR
        destination: ^nft$qtfc_destination,
        local_status: ost$status,
        system_file_name: jmt$system_supplied_name,
        terminated_file: ^nft$qtfc_file;

    /acquire_terminated_output/
      WHILE jmp$terminated_output_exists (jmc$qtf_usage) DO
        jmp$terminate_acquired_output (jmc$qtf_usage, system_file_name, local_status);
        IF local_status.normal THEN
          find_file_and_destination (system_file_name, destination_list, destination, terminated_file);
          IF (terminated_file <> NIL) AND (terminated_file^.transfer_state <> nfc$transfer_initiated) THEN
            delete_file_from_file_list (terminated_file, destination^.file_list, destination^.last_file);
          IFEND;
        ELSE
          nap$display_message (local_status);
          EXIT /acquire_terminated_output/; {----->
        IFEND;
      WHILEND /acquire_terminated_output/;

    /acquire_terminated_input/
      WHILE jmp$terminated_input_exists (jmc$qtf_usage) DO
        jmp$terminate_acquired_input (jmc$qtf_usage, system_file_name, local_status);
        IF local_status.normal THEN
          find_file_and_destination (system_file_name, destination_list, destination, terminated_file);
          IF (terminated_file <> NIL) AND (terminated_file^.transfer_state <> nfc$transfer_initiated) THEN
            delete_file_from_file_list (terminated_file, destination^.file_list, destination^.last_file);
          IFEND;
        ELSE
          nap$display_message (local_status);
          EXIT /acquire_terminated_input/; {----->
        IFEND;
      WHILEND /acquire_terminated_input/;

    /acquire_terminated_generic/
      WHILE jmp$terminated_qfile_exists (nfc$qtf_namve_client_name) DO
        jmp$terminate_acquired_qfile (nfc$qtf_namve_client_name, system_file_name, local_status);
        IF local_status.normal THEN
          find_file_and_destination (system_file_name, destination_list, destination, terminated_file);
          IF (terminated_file <> NIL) AND (terminated_file^.transfer_state <> nfc$transfer_initiated) THEN
            delete_file_from_file_list (terminated_file, destination^.file_list, destination^.last_file);
          IFEND;
        ELSE
          nap$display_message (local_status);
          EXIT /acquire_terminated_generic/;
        IFEND;
      WHILEND /acquire_terminated_generic/;

    PROCEND acquire_terminated_files;
?? TITLE := 'acquire_modified_files', EJECT ??

{ PURPOSE:
{   This procedure gets all the modified output, input and generic queue files
{   from QFM and makes the appropriate changes.

    PROCEDURE acquire_modified_files
      (    store_forward_file_info: nft$store_forward_file_info;
       VAR destination_list: ^nft$qtfc_destination);

      VAR
        current_destination: ^nft$qtfc_destination,
        generic_attribute_keys: ^jmt$qfile_attribute_keys,
        generic_attribute_results: ^jmt$qfile_attribute_results,
        generic_attribute_work_area: ^jmt$work_area,
        generic_work_area_size: ost$segment_length,
        local_status: ost$status,
        modified_generic_descriptor: nft$generic_descriptor,
        modified_input_descriptor: jmt$input_descriptor,
        modified_file: ^nft$qtfc_file,
        modified_output_descriptor: jmt$output_descriptor,
        new_file_destination: ^nft$qtfc_destination;

?? NEWTITLE := 'move_file_to_new_destination', EJECT ??

{ PURPOSE:
{    This procedure removes the specified file from the current
{    destination file list and puts it at the end of the new
{    destination file list.

      PROCEDURE move_file_to_new_destination
        (VAR modified_file: ^nft$qtfc_file;
         VAR current_destination: nft$qtfc_destination;
         VAR new_destination: nft$qtfc_destination);

{ Set all the pointers in the file list to remove the modified file from
{ the old destination file list.

        IF (current_destination.file_list = modified_file) AND
              (current_destination.last_file = modified_file) THEN
          current_destination.file_list := NIL;
          current_destination.last_file := NIL;

        ELSEIF current_destination.file_list = modified_file THEN
          current_destination.file_list := modified_file^.link;
          current_destination.file_list^.back_link := NIL;

        ELSEIF current_destination.last_file = modified_file THEN
          current_destination.last_file := modified_file^.back_link;
          current_destination.last_file^.link := NIL;

        ELSE
          modified_file^.back_link^.link := modified_file^.link;
          modified_file^.link^.back_link := modified_file^.back_link;

        IFEND;

{ Set all the pointers to include the modified file in the new
{ destination file list.

        IF new_destination.file_list = NIL THEN
          new_destination.file_list := modified_file;
          modified_file^.back_link := NIL;

        ELSE
          new_destination.last_file^.link := modified_file;
          modified_file^.back_link := new_destination.last_file;

        IFEND;

        new_destination.last_file := modified_file;
        modified_file^.link := NIL;

      PROCEND move_file_to_new_destination;
?? OLDTITLE, EJECT ??

    /acquire_modified_output/
      WHILE jmp$modified_output_exists (jmc$qtf_usage) DO
        jmp$acquire_modified_output (jmc$qtf_usage, modified_output_descriptor, local_status);
        IF local_status.normal THEN
          find_file_and_destination (modified_output_descriptor.system_file_name, destination_list,
                current_destination, modified_file);
          IF (modified_file = NIL) OR (modified_file^.application_file_descriptor.file_kind <>
                nfc$output_file) THEN

{  INTERNAL QTFC ERROR

            pmp$log('***QTF: Internal error processing modified output file. Proceeding to next file.',
                  local_status);

            CYCLE /acquire_modified_output/; {----->
          ELSEIF  modified_file^.application_file_descriptor.output_descriptor.output_destination <>
                modified_output_descriptor.output_destination THEN
            get_destination (modified_output_descriptor.output_destination, store_forward_file_info,
                  destination_list, new_file_destination);
            move_file_to_new_destination (modified_file, current_destination^, new_file_destination^);
          IFEND;
          modified_file^.application_file_descriptor.output_descriptor := modified_output_descriptor;
          modified_file^.transfer_state := nfc$ready_to_transfer;
        ELSE
          nap$display_message (local_status);
          EXIT /acquire_modified_output/; {----->
        IFEND;
      WHILEND /acquire_modified_output/;

    /acquire_modified_input/
      WHILE jmp$modified_input_exists (jmc$qtf_usage) DO
        jmp$acquire_modified_input (jmc$qtf_usage, modified_input_descriptor, local_status);
        IF local_status.normal THEN
          find_file_and_destination (modified_input_descriptor.system_job_name, destination_list,
                current_destination, modified_file);
          IF (modified_file = NIL) OR (modified_file^.application_file_descriptor.file_kind <> nfc$input_file)
                THEN

{  INTERNAL QTFC ERROR

            pmp$log('***QTF: Internal error processing modified input file. Proceeding to next file.',
                  local_status);

            CYCLE /acquire_modified_input/; {----->
          ELSEIF  modified_file^.application_file_descriptor.input_descriptor.job_destination_family <>
                modified_input_descriptor.job_destination_family THEN
            get_destination (modified_input_descriptor.job_destination_family, store_forward_file_info,
                  destination_list, new_file_destination);
            move_file_to_new_destination (modified_file, current_destination^, new_file_destination^);
          IFEND;
          modified_file^.application_file_descriptor.input_descriptor := modified_input_descriptor;
          modified_file^.transfer_state := nfc$ready_to_transfer;
        ELSE
          nap$display_message (local_status);
          EXIT /acquire_modified_input/; {----->
        IFEND;
      WHILEND /acquire_modified_input/;

      IF jmp$modified_qfile_exists (nfc$qtf_namve_client_name) THEN

        PUSH generic_attribute_keys: [1 .. 2];
        generic_attribute_keys^[1] := jmc$destination;
        generic_attribute_keys^[2] := jmc$remote_host_directive;

        jmp$get_result_size(1, #SEQ(generic_attribute_keys^), generic_work_area_size);

        ALLOCATE generic_attribute_work_area: [[REP generic_work_area_size OF CELL]];

       /acquire_modified_generic/
        WHILE jmp$modified_qfile_exists (nfc$qtf_namve_client_name) DO
          jmp$acquire_modified_qfile (nfc$qtf_namve_client_name, generic_attribute_keys,
                generic_attribute_work_area, generic_attribute_results,
                modified_generic_descriptor.system_file_name, local_status);
          IF local_status.normal THEN

{ Move the generic attributes from the JMT$QFILE_ATTRIBUTE_RESULTS data structure to the
{ NFT$GENERIC_QFILE_DESCRIPTOR's data structure.

            modified_generic_descriptor.destination := generic_attribute_results^[1]^[1].destination;
            modified_generic_descriptor.remote_host_directive :=
                  generic_attribute_results^[1]^[2].remote_host_directive^;

            find_file_and_destination (modified_generic_descriptor.system_file_name, destination_list,
                  current_destination, modified_file);
            IF (modified_file^.application_file_descriptor.file_kind <> nfc$generic_file) OR
                  (modified_file = NIL) THEN

{ INTERNAL QTFC ERROR

          pmp$log('***QTF: Internal error processing modified generic file. Proceeding to next file.',
                  local_status);

              RESET generic_attribute_work_area;
              CYCLE /acquire_modified_generic/;

            ELSEIF  modified_file^.application_file_descriptor.generic_descriptor.destination <>
                  modified_generic_descriptor.destination THEN
              get_destination (modified_generic_descriptor.destination, store_forward_file_info,
                    destination_list, new_file_destination);
              move_file_to_new_destination (modified_file, current_destination^, new_file_destination^);
            IFEND;
            modified_file^.application_file_descriptor.generic_descriptor := modified_generic_descriptor;
            modified_file^.transfer_state := nfc$ready_to_transfer;
          ELSE
            nap$display_message (local_status);
            EXIT /acquire_modified_generic/;
          IFEND;

          RESET generic_attribute_work_area;
        WHILEND /acquire_modified_generic/;

        FREE generic_attribute_work_area;
      IFEND;

    PROCEND acquire_modified_files;
?? TITLE := 'acquire_new_files', EJECT ??

{ PURPOSE:
{   This procedure gets all the new output, input, and generic queue files
{   from QFM and removes the files from the internal tables.

    PROCEDURE acquire_new_files
      (    generic_q_password: jmt$queue_file_password;
           input_q_password: jmt$queue_file_password;
           output_q_password: jmt$queue_file_password;
           store_forward_file_info: nft$store_forward_file_info;
       VAR destination_list: ^nft$qtfc_destination);

      VAR
        destination: ^nft$qtfc_destination,
        generic_attribute_keys: ^jmt$qfile_attribute_keys,
        generic_attribute_results: ^jmt$qfile_attribute_results,
        generic_attribute_work_area: ^jmt$work_area,
        generic_descriptor: nft$generic_descriptor,
        generic_work_area_size: ost$segment_length,
        input_descriptor: jmt$input_descriptor,
        local_status: ost$status,
        message: STRING (256),
        message_length: integer,
        new_qtf_file: nft$qtfc_file,
        output_descriptor: jmt$output_descriptor;

?? NEWTITLE := 'add_file_to_dest_file_list', EJECT ??

{ PURPOSE:
{   This procedure adds a file record to the end of the destination
{   file list and changes the last file pointer of the destination
{   to the new file.

      PROCEDURE add_file_to_dest_file_list
        (    qtf_file: nft$qtfc_file;
         VAR destination: ^nft$qtfc_destination);

        VAR
          new_file: ^nft$qtfc_file;

        ALLOCATE new_file;
        new_file^ := qtf_file;

        IF destination^.file_list = NIL THEN
          destination^.file_list := new_file;
        IFEND;
        IF destination^.last_file <> NIL THEN
          destination^.last_file^.link := new_file;
          new_file^.back_link := destination^.last_file;
        IFEND;
        destination^.last_file := new_file;

      PROCEND add_file_to_dest_file_list;
?? OLDTITLE, EJECT ??
      new_qtf_file.back_link := NIL;
      new_qtf_file.link := NIL;
      new_qtf_file.transfer_state := nfc$ready_to_transfer;
      new_qtf_file.application_file_descriptor.q_file_password := output_q_password;
      new_qtf_file.application_file_descriptor.file_kind := nfc$output_file;

    /acquire_new_output/
      WHILE jmp$new_output_exists (jmc$qtf_usage) DO
        jmp$acquire_new_output (jmc$qtf_usage, output_descriptor, local_status);
        IF local_status.normal THEN
          new_qtf_file.name := output_descriptor.system_file_name;
          new_qtf_file.application_file_descriptor.output_descriptor := output_descriptor;

          get_destination (output_descriptor.output_destination, store_forward_file_info,
                  destination_list, destination);
          add_file_to_dest_file_list (new_qtf_file, destination);
        ELSE
          nap$display_message (local_status);
          EXIT /acquire_new_output/; {----->
        IFEND;
      WHILEND /acquire_new_output/;

      new_qtf_file.application_file_descriptor.q_file_password := input_q_password;
      new_qtf_file.application_file_descriptor.file_kind := nfc$input_file;

    /acquire_new_input/
      WHILE jmp$new_input_exists (jmc$qtf_usage) DO
        jmp$acquire_new_input (jmc$qtf_usage, input_descriptor, local_status);
        IF local_status.normal THEN
          new_qtf_file.name := input_descriptor.system_job_name;
          new_qtf_file.application_file_descriptor.input_descriptor := input_descriptor;

          get_destination (input_descriptor.job_destination_family, store_forward_file_info,
                  destination_list, destination);
          add_file_to_dest_file_list (new_qtf_file, destination);
        ELSE
          nap$display_message (local_status);
          EXIT /acquire_new_input/; {----->
        IFEND;
      WHILEND /acquire_new_input/;

      IF jmp$new_qfile_exists (nfc$qtf_namve_client_name) THEN

        new_qtf_file.application_file_descriptor.file_kind := nfc$generic_file;
        new_qtf_file.application_file_descriptor.q_file_password := generic_q_password;

        PUSH generic_attribute_keys: [1 .. 2];
        generic_attribute_keys^[1] := jmc$destination;
        generic_attribute_keys^[2] := jmc$remote_host_directive;

        jmp$get_result_size(1, #SEQ(generic_attribute_keys^), generic_work_area_size);

        ALLOCATE generic_attribute_work_area: [[REP generic_work_area_size OF CELL]];

        /acquire_new_generic/
        WHILE jmp$new_qfile_exists (nfc$qtf_namve_client_name) DO
          jmp$acquire_new_qfile (nfc$qtf_namve_client_name, generic_attribute_keys,
                generic_attribute_work_area, generic_attribute_results, generic_descriptor.system_file_name,
                local_status);
          IF local_status.normal THEN
            new_qtf_file.name := generic_descriptor.system_file_name;

            generic_descriptor.destination := generic_attribute_results^[1]^[1].destination;

            generic_descriptor.remote_host_directive.parameters :=
                  generic_attribute_results^[1]^[2].remote_host_directive^.parameters;
            generic_descriptor.remote_host_directive.size :=
                  generic_attribute_results^[1]^[2].remote_host_directive^.size;

            IF (#SIZE(generic_descriptor.destination) > 0) AND
                  (generic_descriptor.remote_host_directive.size > 0) THEN
              new_qtf_file.application_file_descriptor.generic_descriptor := generic_descriptor;

              get_destination (generic_descriptor.destination, store_forward_file_info,
                      destination_list, destination);

              add_file_to_dest_file_list (new_qtf_file, destination);
            ELSE

{ The queue file can not be processed because the DESTINATION or REMOTE_HOST_DIRECTIVE was missing.

              STRINGREP(message, message_length, 'Queue file ', generic_descriptor.system_file_name,
                    ' was terminated. DESTINATION or REMOTE_HOST_DIRECTIVE not supplied');
              pmp$log(message(1, message_length), local_status);
              jmp$set_qfile_completed (nfc$qtf_namve_client_name, generic_descriptor.system_file_name,
                    {transfer_complete=} TRUE, local_status);
            IFEND;
          ELSE
            nap$display_message (local_status);
            jmp$set_qfile_completed (nfc$qtf_namve_client_name, generic_descriptor.system_file_name,
                  {transfer_complete=} FALSE, local_status);
            EXIT /acquire_new_generic/;
          IFEND;

          RESET generic_attribute_work_area;

        WHILEND /acquire_new_generic/;

        FREE generic_attribute_work_area;

      IFEND;
    PROCEND acquire_new_files;
?? TITLE := 'get_destination', EJECT ??

{ PURPOSE:
{   This procedure will try to find the destination entry in the list
{   or create a new entry if nothing is found.

    PROCEDURE get_destination
      (    name: ost$name;
           store_forward_file_info: nft$store_forward_file_info;
       VAR destination_list: ^nft$qtfc_destination;
       VAR destination: ^nft$qtfc_destination);

      VAR
        destination_found: boolean;

?? NEWTITLE := 'add_destination_to_list', EJECT ??

{ PURPOSE:
{   This procedure adds a new destination record to the beginning of
{   the destination list.  The new destination record is initialized.

      PROCEDURE add_destination_to_list
        (    name: ost$name;
             store_forward_file_info: nft$store_forward_file_info;
         VAR new_dest: ^nft$qtfc_destination;
         VAR destination_list: ^nft$qtfc_destination);

      VAR
        destination_name: nft$parameter_24_definition,
        ignore_status: ost$status,
        new_target_name: nft$parameter_24_definition,
        target_name_changed: boolean;

        ALLOCATE new_dest;
        new_dest^.back_link := NIL;
        new_dest^.link := NIL;
        new_dest^.name := name;
        new_dest^.next_destination_name.value := osc$null_name;
        new_dest^.next_destination_name.size := nfc$p24_min_param_size;
        new_dest^.file_list := NIL;
        new_dest^.last_file := NIL;
        new_dest^.retry_timer.timer_set := FALSE;
        new_dest^.retry_timer.last_checked := 0;
        new_dest^.retry_timer.time_interval := 0;
        new_dest^.rhfam_timer.timer_set := FALSE;
        new_dest^.rhfam_timer.last_checked := 0;
        new_dest^.rhfam_timer.time_interval := 0;
        new_dest^.transfering_task := NIL;
        new_dest^.translation_request := NIL;

        IF store_forward_file_info.file_open THEN
          destination_name.value := name;
          destination_name.size := clp$trimmed_string_size (name);
          nfp$get_new_destination_name (nfc$sf_qtf_initiator, store_forward_file_info, destination_name,
                target_name_changed, new_target_name, ignore_status);

          IF target_name_changed THEN
            new_dest^.next_destination_name := new_target_name;
          IFEND;
        IFEND;

        IF destination_list <> NIL THEN
          new_dest^.link := destination_list;
          destination_list^.back_link := new_dest;
        IFEND;
        destination_list := new_dest;

      PROCEND add_destination_to_list;
?? OLDTITLE, EJECT ??
      find_destination (name, destination_list, destination, destination_found);

      IF NOT destination_found THEN
        add_destination_to_list (name, store_forward_file_info, destination, destination_list);
      IFEND;

    PROCEND get_destination;
?? OLDTITLE, EJECT ??
    acquire_terminated_files (destination_list);

    acquire_modified_files (store_forward_file_info, destination_list);

    acquire_new_files (generic_q_password, input_q_password, output_q_password, store_forward_file_info,
          destination_list);

  PROCEND acquire_all_q_files;
?? TITLE := 'add_item_to_wait_list', EJECT ??

{ PURPOSE:
{   This procedure adds items to the wait list used by
{   osp$i_await_activity_completion.

  PROCEDURE add_item_to_wait_list
    (    activity: ost$i_activity;
     VAR wait_list: ^ost$i_wait_list);

    VAR
      i: nft$wait_list_size,
      new_seq: ^SEQ ( * ),
      new_wait_list: ^ost$i_wait_list,
      wait_list_limit: nft$wait_list_size;


    wait_list_limit := UPPERBOUND (wait_list^);
    IF (wait_list_limit MOD nfc$wait_list_limit) <> 0 THEN
      RESET qtfc_wait_list_seq;
      NEXT wait_list: [1 .. (wait_list_limit + 1)] IN qtfc_wait_list_seq;
    ELSE
      ALLOCATE new_seq: [[REP (wait_list_limit + nfc$wait_list_limit) OF ost$i_activity]];
      RESET new_seq;
      NEXT new_wait_list: [1 .. (wait_list_limit + 1)] IN new_seq;

      FOR i := LOWERBOUND (wait_list^) TO (wait_list_limit) DO
        new_wait_list^ [i] := wait_list^ [i];
      FOREND;

      FREE qtfc_wait_list_seq;
      qtfc_wait_list_seq := new_seq;
      wait_list := new_wait_list;
    IFEND;

    wait_list^ [wait_list_limit + 1] := activity;

  PROCEND add_item_to_wait_list;
?? TITLE := 'build_namve_connect_data', EJECT ??

{ PURPOSE:
{   This procedure builds connection data for access into gateways
{   if there is user data with the translation.

  PROCEDURE build_namve_connect_data
    (    translation_attributes: ^nat$translation_attributes;
     VAR connection_attributes: ^nat$create_attributes;
     VAR status: ost$status);

    CONST
      nfc$nam_connect_non_cdna = 1,
      nfc$nam_connect_gateway = 2,
      nfc$connect_version_gateway = 1,
      nfc$minimum_size_gat_version_1 = 4;

    TYPE
      gateway_connection_info = packed record
        version: 0 .. 255,
        key: string (2),
      recend;

    VAR
      gateway_connection_data: ^gateway_connection_info,
      user_info: ^string (nac$max_directory_data_length);

    status.normal := TRUE;
    connection_attributes := NIL;

    RESET translation_attributes^ [1].data;
    NEXT user_info IN translation_attributes^ [1].data;
    IF translation_attributes^ [1].data_length > 0 THEN
      IF $INTEGER (user_info^ (1, 1)) <> nfc$connect_version_gateway THEN
        osp$set_status_abnormal (nfc$status_id, nfe$incompatible_address_kind, 'user info version incorrect',
              status);
        nap$display_message (status);
      ELSEIF translation_attributes^ [1].data_length < nfc$minimum_size_gat_version_1 THEN
        osp$set_status_abnormal (nfc$status_id, nfe$incompatible_address_kind,
              'user info for version incorrect length', status);
        nap$display_message (status);
      ELSE
        ALLOCATE connection_attributes: [1 .. 1];
        connection_attributes^ [1].kind := nac$connect_data;
        ALLOCATE connection_attributes^ [1].connect_data: [[REP 1 OF gateway_connection_info]];
        RESET connection_attributes^ [1].connect_data;
        NEXT gateway_connection_data IN connection_attributes^ [1].connect_data;
        gateway_connection_data^.version := nfc$nam_connect_gateway;
        gateway_connection_data^.key := user_info^ (3, 2);
      IFEND;
    IFEND;

  PROCEND build_namve_connect_data;
?? TITLE := 'check_namve_client_status', EJECT ??

{ PURPOSE:
{   This procedure will be called whenever the namve availability
{   timer is up to see if the MANNA application client definition is
{   active.  If it isn't, the timer gets restarted.

  PROCEDURE check_namve_client_status
    (    destination_list: ^nft$qtfc_destination;
     VAR namve_availability: nft$network_availability);

    CONST
      qtf_title_length = qtf_title_part_length + osc$max_name_size,
      qtf_title_part = 'QTFS$',
      qtf_title_part_length = 5;

    VAR
      local_status: ost$status,
      title: ^nat$title_pattern,
      translation_request_id: nat$directory_search_identifier;

    local_status.normal := TRUE;

    IF destination_list <> NIL THEN
      PUSH title: [qtf_title_length];
      title^ (1, qtf_title_part_length) := qtf_title_part;
      title^ (qtf_title_part_length + 1, * ) := destination_list^.name;
      nap$begin_directory_search (title^, nfc$qtf_namve_client_name, {recurrent_search=} FALSE,
            translation_request_id, local_status);
      namve_availability.available := local_status.normal;
    IFEND;

    IF namve_availability.available THEN
      get_activation_date_time (namve_availability.time_stamp);
    ELSE
      nfp$start_timer (0, namve_availability.timer);
    IFEND;

  PROCEND check_namve_client_status;
?? TITLE := 'check_rhfam_sign_on', EJECT ??

{ PURPOSE:
{   This procedure will be called to see if RHFAM is available
{   on the system this task is running on.  If it isn't, the
{   timer gets started or started again.

  PROCEDURE check_rhfam_sign_on
    (VAR rhfam_availability: nft$network_availability;
     VAR rhfam_host_pid: rft$physical_identifier);

    VAR
      ignore_status: ost$status,
      maximum_rhf_connections: rft$application_connections,
      rhfam_status: ost$status;

    maximum_rhf_connections := 0;          {  Really this means unlimited. }
    rfp$application_sign_on (nfc$qtf_rhfam_client_name, rfc$client, maximum_rhf_connections, rhfam_status);
    IF rhfam_status.normal THEN

{ This call is made to get what the PID is defined as in the RHFAM configuration.

      rfp$get_local_host_physical_id (rhfam_host_pid, rhfam_status);
    IFEND;

    rhfam_availability.available := rhfam_status.normal;
    IF rhfam_availability.available THEN
      get_activation_date_time (rhfam_availability.time_stamp);
    ELSE
      rfp$application_sign_off (nfc$qtf_rhfam_client_name, ignore_status);
      nfp$start_timer (0, rhfam_availability.timer);
    IFEND;

  PROCEND check_rhfam_sign_on;
?? TITLE := 'delete_destination_from_list', EJECT ??

{ PURPOSE:
{   This procedure will delete the given destination from the
{   destination list.  Upon exit, DESTINATION = NIL.

  PROCEDURE delete_destination_from_list
    (VAR destination: ^nft$qtfc_destination;
     VAR destination_list: ^nft$qtfc_destination;
     VAR wait_list: ^ost$i_wait_list);

    VAR
      next_destination: ^nft$qtfc_destination,
      prior_destination: ^nft$qtfc_destination;

    find_and_delete_title_request (destination, destination_list, wait_list);

    prior_destination := destination^.back_link;
    next_destination := destination^.link;

    IF prior_destination <> NIL THEN
      prior_destination^.link := destination^.link;
    IFEND;

    IF next_destination <> NIL THEN
      next_destination^.back_link := destination^.back_link;
    IFEND;

    IF destination_list = destination THEN
      destination_list := destination^.link;
    IFEND;

    FREE destination;

  PROCEND delete_destination_from_list;
?? TITLE := 'delete_file_from_file_list', EJECT ??

{ PURPOSE:
{   This procedure deletes the specified file from the specified file
{   list.  When this procedure exits, DELETED_FILE = NIL.

  PROCEDURE delete_file_from_file_list
    (VAR deleted_file: ^nft$qtfc_file;
     VAR file_list: ^nft$qtfc_file;
     VAR last_file: ^nft$qtfc_file);

    VAR
      back_link_file: ^nft$qtfc_file,
      link_file: ^nft$qtfc_file;

    back_link_file := deleted_file^.back_link;
    link_file := deleted_file^.link;

    IF back_link_file <> NIL THEN
      back_link_file^.link := deleted_file^.link;
    IFEND;

    IF link_file <> NIL THEN
      link_file^.back_link := deleted_file^.back_link;
    IFEND;

    IF deleted_file = file_list THEN
      file_list := deleted_file^.link;
    IFEND;

    IF deleted_file = last_file THEN
      last_file := deleted_file^.back_link;
    IFEND;

    FREE deleted_file;

  PROCEND delete_file_from_file_list;
?? TITLE := 'delete_item_from_wait_list', EJECT ??

{ PURPOSE:
{   This procedure removes activity items from the wait list used
{   by osp$i_await_activity_completion.
{
{ NOTES:
{   The parameter INDEX is an integer because of OSP$I_AWAIT_ACTIVITY_COMPLETION.
{
{   4 is used because the first 2 activities in the wait list should
{   always be in the wait list.  If last_index = 3 then the last
{   activity is just removed.  INDEX should never be 1 or 2.

  PROCEDURE delete_item_from_wait_list
    (    destination_list: ^nft$qtfc_destination;
         index: integer;
     VAR wait_list: ^ost$i_wait_list);

    VAR
      destination: ^nft$qtfc_destination,
      last_index: nft$wait_list_size;

    last_index := UPPERBOUND (wait_list^);

    IF (last_index >= 4) AND (last_index <> index) THEN
      wait_list^ [index] := wait_list^ [last_index];

{ update the destination_list pointer to the outstanding title translation request

      destination := destination_list;

   /update_destination_title_req/
      WHILE destination <> NIL DO
        IF destination^.translation_request <> NIL THEN
          IF wait_list^ [last_index].translation_request = destination^.translation_request^
                .translation_request THEN
            destination^.translation_request := ^wait_list^ [index];
            EXIT /update_destination_title_req/;
          IFEND;
        IFEND;
        destination := destination^.link;
      WHILEND /update_destination_title_req/;
    IFEND;

    wait_list^ [last_index].activity := osc$i_null_activity;
    RESET qtfc_wait_list_seq;
    NEXT wait_list: [1 .. (last_index - 1)] IN qtfc_wait_list_seq;

  PROCEND delete_item_from_wait_list;
?? TITLE := 'establish_server_connection', EJECT ??

{ PURPOSE:
{   This procedure is called to set up a connection with the remote destination.

  PROCEDURE establish_server_connection
    (    destination: ^nft$qtfc_destination;
         destination_list: ^nft$qtfc_destination;
     VAR rhfam_availability: nft$network_availability;
     VAR namve_availability: nft$network_availability;
     VAR connection: nft$qtf_connection;
     VAR wait_list: ^ost$i_wait_list);

    VAR
      connection_status: ost$status,
      ignore_status: ost$status,
      latest_time_check: nft$micro_second,
      unique_name: ost$name;

?? NEWTITLE := 'establish_namve_connection', EJECT ??

{ PURPOSE:
{   This procedure does all the initial steps to set up a NAM/VE
{   connection with a QTF Server on a remote system.  If the QTFS on
{   the remote system is found through the title translation, a call
{   is made request_namve_connection to finish establishing the
{   connection.

    PROCEDURE establish_namve_connection
      (    destination: ^nft$qtfc_destination;
       VAR connection: nft$qtf_connection;
       VAR wait_list: ^ost$i_wait_list;
       VAR namve_availability: nft$network_availability;
       VAR status: ost$status);

      CONST
        translation_wait_time = 15000;      { milliseconds }

      VAR
        activity: ost$i_activity,
        connection_attributes: ^nat$create_attributes,
        directory_data: ^nat$directory_data,
        error_message_string: string(132),
        error_message_string_length: integer,
        ignore_status: ost$status,
        translation_address: nat$network_address,
        translation_attributes: ^nat$translation_attributes,
        translation_request_id: nat$directory_search_identifier;

      status.normal := TRUE;

      IF destination^.next_destination_name.value = osc$null_name THEN
        issue_title_translation (destination^.name, translation_request_id, namve_availability, status);
      ELSE
         issue_title_translation (destination^.next_destination_name.value, translation_request_id,
               namve_availability, status);
      IFEND;
      IF status.normal THEN
        PUSH directory_data: [[REP nac$max_directory_data_length OF cell]];
        PUSH translation_attributes: [1 .. 1];
        translation_attributes^ [1].selector := nac$translation_data;
        translation_attributes^ [1].data := directory_data;
        nap$get_title_translation (translation_request_id, translation_wait_time, translation_attributes,
              translation_address, status);
        IF status.normal THEN
          nap$end_directory_search (translation_request_id, ignore_status);

          build_namve_connect_data (translation_attributes, connection_attributes, status);
          IF status.normal THEN
            request_namve_connection (translation_address, connection_attributes, connection, status);
            IF NOT status.normal THEN
              amp$return (connection.file_name, ignore_status);
              connection.kind := nfc$unknown_network;
              IF (status.condition = nae$unknown_application) OR
                    (status.condition = nae$application_inactive) OR
                    (status.condition = nae$network_inactive) THEN
                namve_availability.available := FALSE;
                nfp$start_timer (nfc$one_minute, namve_availability.timer);
              ELSE

{ log the bad status to the SYSTEM's job_log and place this destination into a delayed retry condition
{ because the title translation was successful but the connection was not successful.

                IF destination^.next_destination_name.value = osc$null_name THEN
                  STRINGREP(error_message_string, error_message_string_length,
                        '***QTF/EstNC: Connection request failed for title ',
                        destination^.name(1, clp$trimmed_string_size(destination^.name)),
                        ' with the following status:');
                ELSE
                  STRINGREP(error_message_string, error_message_string_length,
                        '***QTF/EstNC: Connection request failed for title ',
                        destination^.next_destination_name.value(1, clp$trimmed_string_size(
                        destination^.next_destination_name.value)), ' with the following status:');
                IFEND;
                pmp$log (error_message_string(1, error_message_string_length), ignore_status);
                nap$display_message (status);

                nfp$start_timer (0, destination^.retry_timer);
              IFEND;
            IFEND;
          IFEND;
          IF connection_attributes <> NIL THEN
            FREE connection_attributes^ [1].connect_data;
            FREE connection_attributes;
          IFEND;
        ELSE
          activity.activity := nac$i_await_title_translation;
          activity.translation_request := translation_request_id;
          add_item_to_wait_list (activity, wait_list);
          destination^.translation_request := ^wait_list^ [UPPERBOUND (wait_list^)];
        IFEND;
      IFEND;

    PROCEND establish_namve_connection;
?? TITLE := 'establish_rhfam_connection', EJECT ??

{ PURPOSE:
{   This procedure does all the initial steps to set up a RHFAM
{   connection with a QTF Server on a remote system.  If the QTFS on
{   the remote system is found through the title translation, a call
{   is made request_rhfam_connection to finish establishing the
{   connection.

    PROCEDURE establish_rhfam_connection
      (    destination: ^nft$qtfc_destination;
       VAR connection: nft$qtf_connection;
       VAR rhfam_availability: nft$network_availability;
       VAR status: ost$status);

      VAR
        ignore_status: ost$status;

      status.normal := TRUE;

      IF destination^.next_destination_name.value = osc$null_name THEN
        request_rhfam_connection (destination^.name, connection, status);
      ELSE
        request_rhfam_connection (destination^.next_destination_name.value, connection, status);
      IFEND;
      IF (NOT status.normal) AND (status.condition = rfe$system_task_not_active) THEN
        rhfam_availability.available := FALSE;
        amp$return (connection.file_name, ignore_status);
        connection.kind := nfc$unknown_network;
        rfp$application_sign_off (nfc$qtf_rhfam_client_name, ignore_status);
        nfp$start_timer (0, rhfam_availability.timer);
      ELSEIF NOT status.normal THEN
        amp$return (connection.file_name, ignore_status);
        connection.kind := nfc$unknown_network;
        nfp$start_timer (nfc$one_minute, destination^.rhfam_timer);
      ELSE
        destination^.rhfam_timer.timer_set := FALSE;
        destination^.rhfam_timer.last_checked := 0;
        destination^.rhfam_timer.time_interval := 0;
      IFEND;

    PROCEND establish_rhfam_connection;
?? OLDTITLE, EJECT ??
    latest_time_check := #FREE_RUNNING_CLOCK (0);

    pmp$get_unique_name (unique_name, ignore_status);
    connection.file_name := unique_name;
    connection.kind := nfc$unknown_network;

    IF rhfam_availability.available AND nfp$timer_expired (destination^.rhfam_timer, latest_time_check) THEN
      establish_rhfam_connection (destination, connection, rhfam_availability, connection_status);
      IF connection_status.normal THEN
        find_and_delete_title_request (destination, destination_list, wait_list);
      IFEND;
    IFEND;
    IF (connection.kind = nfc$unknown_network) AND (destination^.translation_request = NIL) AND
          namve_availability.available THEN
      establish_namve_connection (destination, connection, wait_list, namve_availability,
            connection_status);
    IFEND;

  PROCEND establish_server_connection;
?? TITLE := 'find_and_delete_title_request', EJECT ??

{ PURPOSE:
{   This procedure will find the title request for this destination in the
{   wait list and delete it.  Upon exit, DESTINATION.TRANSLATION_REQUEST = NIL.

  PROCEDURE find_and_delete_title_request
    (    destination: ^nft$qtfc_destination;
         destination_list: ^nft$qtfc_destination;
     VAR wait_list: ^ost$i_wait_list);

    CONST
      first_element = 3;

    VAR
      found: boolean,
      ignore_status: ost$status,
      index: nft$wait_list_size,
      last_index: nft$wait_list_size;

    IF destination^.translation_request <> NIL THEN
      IF destination^.translation_request^.activity = nac$i_await_title_translation THEN
        nap$end_directory_search (destination^.translation_request^.translation_request, ignore_status);

        found := FALSE;
        index := first_element;
        last_index := UPPERBOUND(wait_list^);

        REPEAT
          IF ((wait_list^[index].activity = nac$i_await_title_translation) AND
                (destination^.translation_request^.translation_request =
                wait_list^ [index].translation_request)) THEN
            delete_item_from_wait_list (destination_list, index, wait_list);
            found := TRUE;
          IFEND;
          index := index + 1;
        UNTIL found OR (index > last_index);
      IFEND;
      destination^.translation_request := NIL;
    IFEND;
  PROCEND find_and_delete_title_request;
?? TITLE := 'find_destination', EJECT ??

{ PURPOSE:
{   Given a name, this procedure will attempt to find a destination
{   with that name in the destination list.

  PROCEDURE find_destination
    (    destination_name: ost$name;
         destination_list: ^nft$qtfc_destination;
     VAR destination: ^nft$qtfc_destination;
     VAR destination_found: boolean);

    destination := destination_list;
    destination_found := FALSE;

    WHILE (NOT destination_found) AND (destination <> NIL) DO
      destination_found := destination_name = destination^.name;
      IF NOT destination_found THEN
        destination := destination^.link;
      IFEND;
    WHILEND;

  PROCEND find_destination;
?? TITLE := 'find_file_and_destination', EJECT ??

{ PURPOSE:
{   This procedure finds a file and the destination it belongs to
{   using the system supplied file name.

  PROCEDURE find_file_and_destination
    (    system_file_name: jmt$system_supplied_name;
         destination_list: ^nft$qtfc_destination;
     VAR destination: ^nft$qtfc_destination;
     VAR requested_file: ^nft$qtfc_file);

    VAR
      file_found: boolean;

    file_found := FALSE;

    destination := destination_list;
    WHILE (NOT file_found) AND (destination <> NIL) DO
      requested_file := destination^.file_list;

      WHILE (NOT file_found) AND (requested_file <> NIL) DO
        file_found := system_file_name = requested_file^.name;
        IF NOT file_found THEN
          requested_file := requested_file^.link;
        IFEND;
      WHILEND;

      IF NOT file_found THEN
        destination := destination^.link;
      IFEND;
    WHILEND;

  PROCEND find_file_and_destination;
?? TITLE := 'get_activation_date_time', EJECT ??

{ PURPOSE:
{   This procedure will get the current date and time in a coded format.

  PROCEDURE get_activation_date_time (VAR activation_date_time: nft$bcd_time);

    VAR
      date_time: ost$date_time,
      ignore_status: ost$status;

    pmp$get_compact_date_time (date_time, ignore_status);

    activation_date_time.date.year1 := (date_time.year MOD 100) DIV 10;
    activation_date_time.date.year2 := date_time.year MOD 10;
    activation_date_time.date.month1 := date_time.month DIV 10;
    activation_date_time.date.month2 := date_time.month MOD 10;
    activation_date_time.date.day1 := date_time.day DIV 10;
    activation_date_time.date.day2 := date_time.day MOD 10;
    activation_date_time.time.hours1 := date_time.hour DIV 10;
    activation_date_time.time.hours2 := date_time.hour MOD 10;
    activation_date_time.time.minutes1 := date_time.minute DIV 10;
    activation_date_time.time.minutes2 := date_time.minute MOD 10;
    activation_date_time.time.seconds1 := date_time.second DIV 10;
    activation_date_time.time.seconds2 := date_time.second MOD 10;
    activation_date_time.time.milliseconds1 := date_time.millisecond DIV 100;
    activation_date_time.time.milliseconds2 := (date_time.millisecond MOD 100) DIV 10;
    activation_date_time.time.milliseconds3 := date_time.millisecond MOD 10;
    activation_date_time.time.fill := 0;

  PROCEND get_activation_date_time;
?? TITLE := 'issue_title_translation', EJECT ??

{ PURPOSE:
{   This procedure will issue a title translation request for the
{   given destination name.  If the client application has been
{   deactivated or does not exsist, NAM/VE will be considered un-
{   available.

  PROCEDURE issue_title_translation
    (    destination_name: ost$name;
     VAR translation_request_id: nat$directory_search_identifier;
     VAR namve_availability: nft$network_availability;
     VAR status: ost$status);

    CONST
      qtf_title_length = qtf_title_part_length + osc$max_name_size,
      qtf_title_part = 'QTFS$',
      qtf_title_part_length = 5;

    VAR
      title: ^nat$title_pattern;

    status.normal := TRUE;

    PUSH title: [qtf_title_length];
    title^ (1, qtf_title_part_length) := qtf_title_part;
    title^ (qtf_title_part_length + 1, * ) := destination_name;
    nap$begin_directory_search (title^, nfc$qtf_namve_client_name, {recurrent_search=} TRUE,
          translation_request_id, status);
    IF (NOT status.normal) AND ((status.condition = nae$unknown_application) OR
          (status.condition = nae$application_inactive) OR (status.condition = nae$network_inactive)) THEN
      namve_availability.available := FALSE;
      nfp$start_timer (nfc$one_minute, namve_availability.timer);
    IFEND;

  PROCEND issue_title_translation;
?? TITLE := 'process_intertask_message', EJECT ??

{ PURPOSE:
{   This procedure receives messages from the QTFI tasks.  Based on
{   what the message indicates, this procedure will take the appropriate
{   action and probably send a message back to the QTFI task.

  PROCEDURE process_intertask_message
    (    destination_list: ^nft$qtfc_destination;
         namve_host_pid: ost$name;
         rhfam_host_pid: rft$physical_identifier;
         store_forward_file_info: nft$store_forward_file_info;
     VAR namve_availability: nft$network_availability;
     VAR number_of_running_qtfi_tasks: ost$non_negative_integers;
     VAR rhfam_availability: nft$network_availability;
     VAR wait_list: ^ost$i_wait_list;
     VAR single_transfer_per_connection: boolean);

    CONST
      decrement_task_count = TRUE,
      free_task_information = TRUE,
      keep_task_information = FALSE,
      no_decrement_task_count = FALSE;

    VAR
      connection: nft$qtf_connection,
      current_task: ^nft$qtfi_task,
      destination: ^nft$qtfc_destination,
      ignore_status: ost$status,
      new_destination: ^nft$qtfc_destination,
      new_destination_task: ^nft$qtfi_task,
      task_msg: nft$intertask_message,
      task_msg_size: nft$intertask_transfer_size,
      transfer_count: nft$intertask_transfer_size,
      transfer_file: ^nft$qtfc_file,
      status: ost$status;
?? NEWTITLE := 'decode_file_transfer_status', EJECT ??

{ PURPOSE:
{   This procedure is given a file transfer status message and decides
{   what actions to take based on the file transfer status.

    PROCEDURE decode_file_transfer_status
      (    task_msg: nft$intertask_message;
           destination: ^nft$qtfc_destination;
           current_task: ^nft$qtfi_task;
           namve_host_pid: ost$name;
           rhfam_host_pid: rft$physical_identifier;
           store_forward_file_info: nft$store_forward_file_info;
           single_transfer_per_connection: boolean;
       VAR status: ost$status);

      VAR
        new_transfer_file: ^nft$qtfc_file,
        normal_transfer_complete: boolean,
        transferred_file: ^nft$qtfc_file;

      status.normal := TRUE;

      transferred_file := current_task^.file_in_transfer;
      IF transferred_file <> NIL THEN
        IF transferred_file^.name <> task_msg.qtf_system_file_name THEN

{ TROUBLE

          RETURN; {----->
        IFEND;

        normal_transfer_complete := (task_msg.qtf_transfer_status = nfc$qtf_transfer_complete) OR
              (task_msg.qtf_transfer_status = nfc$qtf_transfer_failed_noretry);
        CASE task_msg.qtf_transfer_status OF
        = nfc$qtf_transfer_complete, nfc$qtf_transfer_failed_noretry =
          IF single_transfer_per_connection THEN
            send_terminate_connection (current_task^.transfer_connection, current_task^.task_id,
                  current_task^.last_message_sent, status);
          ELSE
            select_file_for_transfer (destination, current_task^.transfer_connection, namve_host_pid,
                  rhfam_host_pid, store_forward_file_info, new_transfer_file);
            current_task^.file_in_transfer := new_transfer_file;
            IF new_transfer_file <> NIL THEN
              send_file_transfer_msg_to_task (new_transfer_file, current_task^.transfer_connection,
                    namve_host_pid, rhfam_host_pid, current_task^.task_id, current_task^.last_message_sent,
                    current_task^.file_transfer_start_time, status);
            ELSE
              send_terminate_connection (current_task^.transfer_connection, current_task^.task_id,
                    current_task^.last_message_sent, status);
            IFEND;
          IFEND;

        = nfc$qtf_transfer_failed_retry =
          send_terminate_connection (current_task^.transfer_connection, current_task^.task_id,
                current_task^.last_message_sent, status);

{ set up transferred file to attempt retry later.

          nfp$start_timer (0, destination^.retry_timer);

        = nfc$qtf_transfer_aborted =
          send_terminate_connection (current_task^.transfer_connection, current_task^.task_id,
                current_task^.last_message_sent, status);

        ELSE
          ;
        CASEND;
        notify_qfm_and_delete_file (normal_transfer_complete, transferred_file, destination^.file_list,
              destination^.last_file);
      ELSE

{ Send out error message. This file is in DEEP WEEDS.

      IFEND;

    PROCEND decode_file_transfer_status;
?? TITLE := 'notify_qfm_and_delete_file', EJECT ??

{ PURPOSE:
{   This procedure notifies QFM of the completion status of a file
{   that has been transfered.  The file will be deleted from QTFC's
{   internal tables.  QFM may give the same file back later to try
{   another transfer.

    PROCEDURE notify_qfm_and_delete_file
      (    transfer_complete: boolean;
       VAR transferred_file: ^nft$qtfc_file;
       VAR file_list: ^nft$qtfc_file;
       VAR last_file: ^nft$qtfc_file);

      VAR
        ignore_status: ost$status;

      IF transferred_file^.application_file_descriptor.file_kind = nfc$output_file THEN
        jmp$set_output_completed (jmc$qtf_usage, transferred_file^.name, transfer_complete, ignore_status);
      ELSEIF transferred_file^.application_file_descriptor.file_kind = nfc$input_file THEN
        jmp$set_input_completed (jmc$qtf_usage, transferred_file^.name, transfer_complete, ignore_status);
      ELSEIF transferred_file^.application_file_descriptor.file_kind = nfc$generic_file THEN
        jmp$set_qfile_completed (nfc$qtf_namve_client_name, transferred_file^.name, transfer_complete,
              ignore_status);
      IFEND;

      delete_file_from_file_list (transferred_file, destination^.file_list, destination^.last_file);

    PROCEND notify_qfm_and_delete_file;
?? TITLE := 'delete_qtfi_task', EJECT ??

{ PURPOSE:
{   This procedure will remove the specified QTFI task from the task
{   list because the task has terminated.
{
{ NOTES:
{   When this procedure returns, current task will point to the next
{   task entry in the task list.

    PROCEDURE delete_qtfi_task
      (    free_task_information: boolean;
           decrement_task_count: boolean;
       VAR current_task: ^nft$qtfi_task;
       VAR number_of_running_qtfi_tasks: ost$non_negative_integers;
       VAR qtfi_task_list: ^nft$qtfi_task);

      VAR
        next_task: ^nft$qtfi_task,
        prior_task_task: ^nft$qtfi_task,
        task_to_free: ^nft$qtfi_task;

      prior_task_task := current_task^.back_link;
      next_task := current_task^.link;
      task_to_free := current_task;

      IF prior_task_task <> NIL THEN
        prior_task_task^.link := current_task^.link;
      IFEND;

      IF next_task <> NIL THEN
        next_task^.back_link := current_task^.back_link;
      IFEND;

      IF qtfi_task_list = current_task THEN
        qtfi_task_list := current_task^.link;
      IFEND;

      IF free_task_information THEN
        FREE task_to_free;
      IFEND;

      IF decrement_task_count THEN
        number_of_running_qtfi_tasks := number_of_running_qtfi_tasks - 1;
      IFEND;
      current_task := next_task;

    PROCEND delete_qtfi_task;
?? TITLE := 'find_idle_destination', EJECT ??

{ PURPOSE:
{   This procedure finds a destination without a connection file and
{   no QTFI task.  This will be only be effective when there is a
{   limit on the number of QTFI tasks that can be running at the same
{   time.

    PROCEDURE find_idle_destination
      (    destination_list: ^nft$qtfc_destination;
       VAR connection: nft$qtf_connection;
       VAR namve_availability: nft$network_availability;
       VAR rhfam_availability: nft$network_availability;
       VAR destination: ^nft$qtfc_destination;
       VAR wait_list: ^ost$i_wait_list);

      VAR
        destination_found: boolean;

      destination_found := FALSE;
      destination := destination_list;
      WHILE (NOT destination_found) AND (destination <> NIL) DO
        destination_found := (destination^.transfering_task = NIL) AND
              (destination^.file_list <> NIL) AND (destination^.translation_request = NIL) AND
              nfp$timer_expired (destination^.retry_timer, #FREE_RUNNING_CLOCK (0));
        IF destination_found THEN
          destination^.retry_timer.timer_set := FALSE;
          destination^.retry_timer.last_checked := 0;
          destination^.retry_timer.time_interval := 0;

          establish_server_connection (destination, destination_list, rhfam_availability, namve_availability,
              connection, wait_list);
          destination_found := (connection.kind <> nfc$unknown_network);
        IFEND;
        IF NOT destination_found THEN
          destination := destination^.link;
        IFEND;
      WHILEND;

    PROCEND find_idle_destination;
?? TITLE := 'send_terminate_connection', EJECT ??

{ PURPOSE:
{   This procedure will send a message to a QTFI task telling the task
{   to close and return the connection.

    PROCEDURE send_terminate_connection
      (    connection: nft$qtf_connection;
           task_id: pmt$task_id;
       VAR last_message_sent: nft$intertask_message_kind;
       VAR status: ost$status);

      VAR
        qtfi_task_msg: nft$intertask_message,
        retry_count: nft$qtf_put_async_retry_range;

      status.normal := TRUE;
      qtfi_task_msg.kind := nfc$qtf_terminate_connection;
      qtfi_task_msg.connect_file_name := connection.file_name;
      retry_count := 0;

      REPEAT
        nfp$put_async_task_message (task_id, ^qtfi_task_msg, #SIZE (qtfi_task_msg), status);
        IF status.normal THEN
          last_message_sent := nfc$qtf_terminate_connection;
        ELSE
          pmp$wait (nfc$qtf_put_async_wait_time, nfc$qtf_put_async_wait_time);
          retry_count := retry_count + 1;
        IFEND;
      UNTIL (status.normal) OR (retry_count > nfc$qtf_put_async_max_retry);

    PROCEND send_terminate_connection;
?? TITLE := 'send_terminate_task_msg', EJECT ??

{ PURPOSE:
{   This procedure will send a message to a QTFI task telling the task
{   to terminate itself.

    PROCEDURE send_terminate_task_msg
      (    task_id: pmt$task_id;
       VAR last_message_sent: nft$intertask_message_kind;
       VAR status: ost$status);

      VAR
        qtfi_task_msg: nft$intertask_message,
        retry_count: nft$qtf_put_async_retry_range;

      status.normal := TRUE;
      qtfi_task_msg.kind := nfc$qtf_terminate_task;
      retry_count := 0;

      REPEAT
        nfp$put_async_task_message (task_id, ^qtfi_task_msg, #SIZE (qtfi_task_msg), status);
        IF status.normal THEN
          last_message_sent := nfc$qtf_terminate_task;
        ELSE
          pmp$wait (nfc$qtf_put_async_wait_time, nfc$qtf_put_async_wait_time);
          retry_count := retry_count + 1;
        IFEND;
      UNTIL (status.normal) OR (retry_count > nfc$qtf_put_async_max_retry);

    PROCEND send_terminate_task_msg;
?? OLDTITLE, EJECT ??
    task_msg_size := #SIZE (nft$intertask_message);
    destination := destination_list;
    WHILE destination <> NIL DO
      current_task := destination^.transfering_task;

{ All tasks have to be checked because there is no way of knowing which task
{ sent the intertask message.

      WHILE current_task <> NIL DO
        nfp$get_async_task_message (current_task^.task_id, ^task_msg, task_msg_size, 0, transfer_count,
              status);
        IF NOT status.normal THEN
          delete_qtfi_task (free_task_information, decrement_task_count, current_task,
                number_of_running_qtfi_tasks, destination^.transfering_task);
        ELSEIF (status.normal) AND (transfer_count > 0) THEN
          CASE task_msg.kind OF

{ These kinds of messages should not be received by QTFC or QTFI, they
{ are messages between BTF and SCF or NTF.

          = nfc$btf_file_transfer, nfc$btf_file_transfer_status,

{ These kinds of messages are sent to QTFI and should not be received.

          nfc$qtf_file_transfer, nfc$qtf_terminate_connection, nfc$qtf_terminate_task =
            current_task := current_task^.link;

          = nfc$qtf_file_transfer_status =
            IF NOT task_msg.qtf_task_status.normal THEN
              nap$display_message (task_msg.qtf_task_status);
            IFEND;
            current_task^.file_transfer_start_time := 0;
            decode_file_transfer_status (task_msg, destination, current_task, namve_host_pid,
                  rhfam_host_pid, store_forward_file_info, single_transfer_per_connection, status);
            current_task := current_task^.link;

          = nfc$qtf_connection_terminated =
            amp$return (current_task^.transfer_connection.file_name, ignore_status);
            current_task^.transfer_connection.file_name := osc$null_name;
            current_task^.transfer_connection.kind := nfc$unknown_network;

            find_idle_destination (destination_list, connection, namve_availability, rhfam_availability,
                  new_destination, wait_list);
            IF new_destination <> NIL THEN

{ Save the current task information from the old destination
{ so that it can be moved to the new (idle) destination

              new_destination_task := current_task;
              delete_qtfi_task (keep_task_information, no_decrement_task_count, current_task,
                    number_of_running_qtfi_tasks, destination^.transfering_task);
              new_destination_task^.back_link := NIL;
              new_destination_task^.link := NIL;
              new_destination_task^.file_in_transfer := NIL;
              new_destination_task^.file_transfer_start_time := 0;
              new_destination_task^.transfer_connection := connection;
              IF new_destination^.transfering_task <> NIL THEN
                new_destination^.transfering_task^.back_link := new_destination_task;
                new_destination_task^.link := new_destination^.transfering_task;
              IFEND;
              new_destination^.transfering_task := new_destination_task;

              select_file_for_transfer (new_destination, connection, namve_host_pid, rhfam_host_pid,
                    store_forward_file_info, transfer_file);
              new_destination_task^.file_in_transfer := transfer_file;
              IF transfer_file <> NIL THEN
                send_file_transfer_msg_to_task (transfer_file, new_destination_task^.transfer_connection,
                      namve_host_pid, rhfam_host_pid, new_destination_task^.task_id,
                      new_destination_task^.last_message_sent, new_destination_task^.file_transfer_start_time,
                      status);
              ELSE
                send_terminate_task_msg (new_destination_task^.task_id,
                      new_destination_task^.last_message_sent, status);
              IFEND;
            ELSE
              send_terminate_task_msg (current_task^.task_id, current_task^.last_message_sent, status);
              current_task := current_task^.link;
            IFEND;

          = nfc$qtf_task_terminated =
            delete_qtfi_task (free_task_information, decrement_task_count, current_task,
                  number_of_running_qtfi_tasks, destination^.transfering_task);

          = nfc$abnormal_child_task_abort =
            IF (current_task^.last_message_sent = nfc$qtf_file_transfer) AND (current_task^.file_in_transfer
                  <> NIL) THEN
              notify_qfm_and_delete_file (FALSE, current_task^.file_in_transfer, destination^.file_list,
                    destination^.last_file);
            IFEND;

            IF current_task^.transfer_connection.kind <> nfc$unknown_network THEN
              amp$return (current_task^.transfer_connection.file_name, ignore_status);
              current_task^.transfer_connection.file_name := osc$null_name;
              current_task^.transfer_connection.kind := nfc$unknown_network;
            IFEND;

            delete_qtfi_task (free_task_information, decrement_task_count, current_task,
                  number_of_running_qtfi_tasks, destination^.transfering_task);

          ELSE
            ;
          CASEND;
        ELSE
          current_task := current_task^.link;
        IFEND;
      WHILEND;
      destination := destination^.link;
    WHILEND;

  PROCEND process_intertask_message;
?? TITLE := 'remove_translation_requests', EJECT ??

{ PURPOSE:
{   This procedure will remove any title translation requests from
{   the wait list if the MANNA application definition is deactivated
{   while QTFC is running.
{
{ NOTES:
{   This procedure does not look at the first two elements in the
{   wait list because they should always be there.
{   The upperbound of WAIT_LIST^ should always be calculated because
{   the size of the wait list will be changing as activities are
{   removed.

  PROCEDURE remove_translation_requests
    (    destination_list: ^nft$qtfc_destination;
     VAR wait_list: ^ost$i_wait_list);

    CONST
      first_element = 3;

    VAR
      destination: ^nft$qtfc_destination,
      ignore_status: ost$status,
      index: nft$wait_list_size;

    index := first_element;
    WHILE index <= UPPERBOUND (wait_list^) DO
      IF wait_list^ [index].activity = nac$i_await_title_translation THEN
        nap$end_directory_search (wait_list^ [index].translation_request, ignore_status);

{ clear out (set to NIL) the destination_list pointer to the removed title translation request

        destination := destination_list;

     /clear_out_destination_title_req/
        WHILE destination <> NIL DO
          IF destination^.translation_request <> NIL THEN
            IF wait_list^ [index].translation_request = destination^.translation_request^.translation_request
                  THEN
              destination^.translation_request := NIL;
              EXIT /clear_out_destination_title_req/;
            IFEND;
          IFEND;
          destination := destination^.link;
        WHILEND /clear_out_destination_title_req/;

        delete_item_from_wait_list (destination_list, index, wait_list);
      ELSE
        index := index + 1;
      IFEND;
    WHILEND;

  PROCEND remove_translation_requests;
?? TITLE := 'request_namve_connection', EJECT ??

{ PURPOSE:
{   This procedure will establish a connection with QTFS on a remote
{   mainframe via NAM/VE.
{
{ NOTES:
{   This procedure does not wait for a server reponse or open the
{   connection file.  QTFI will wait for a server reponse, set up the
{   file attributes and open the file.  QTFC will pass the connection
{   file name to QTFI.

  PROCEDURE request_namve_connection
    (    service_address: nat$network_address;
         connection_attributes: ^nat$create_attributes;
     VAR connection: nft$qtf_connection;
     VAR status: ost$status);

    status.normal := TRUE;

    nap$request_connection (service_address, nfc$qtf_namve_client_name, connection.file_name,
          nac$cdna_session, connection_attributes, nfc$half_minute, status);
    IF status.normal THEN
      connection.kind := nfc$network_nam;
    IFEND;

  PROCEND request_namve_connection;
?? TITLE := 'request_rhfam_connection', EJECT ??

{ PURPOSE:
{   This procedure will establish a connection with QTFS on a remote
{   mainframe via RHFAM.
{
{ NOTES:
{   This procedure does not wait for a server reponse or open the
{   connection file.  QTFI will wait for a server reponse, set up the
{   file attributes and open the file.  QTFC will pass the connection
{   file name to QTFI.

  PROCEDURE request_rhfam_connection
    (    destination_name: ost$name;
     VAR connection: nft$qtf_connection;
     VAR status: ost$status);

    CONST
      wait_time = 2 * 60 * 1000;    { 2 minutes in milliseconds }

    VAR
      destination_host: rft$host_identifier,
      host_ids: rft$destination_hosts,
      i: rft$number_of_hosts,
      number_of_hosts: rft$number_of_hosts,
      server_response: rft$server_response;

    status.normal := TRUE;

    destination_host.host_identifier_kind := rfc$logical_identifier;
    destination_host.logical_identifier := destination_name;

    rfp$find_available_service (nfc$rhfam_service_name, destination_host, host_ids, number_of_hosts, status);
    IF status.normal THEN
      FOR i := LOWERBOUND (host_ids) TO number_of_hosts DO
        rfp$request_connection (nfc$qtf_rhfam_client_name, nfc$rhfam_service_name, host_ids [i],
              connection.file_name, {connect_file_attributes=} NIL, status);
        IF status.normal THEN
          rfp$await_server_response (connection.file_name, wait_time, server_response, status);
          IF status.normal  AND (server_response.server_response_kind = rfc$accept) THEN
            connection.kind := nfc$network_lcn;

{ The remote destination has been found and the remote server responded positively;
{ there is no need to look further.

            RETURN; {----->
          IFEND;
        IFEND;
      FOREND;
    IFEND;

  PROCEND request_rhfam_connection;
?? TITLE := 'satisfy_title_translation', EJECT ??

{ PURPOSE:
{   This procedure is called whenever a title translation request is
{   satisfied or becomes stale.  If the request is satisfied, a
{   connection is established.
{
{ NOTES:
{   The parameter ACTIVITY_INDEX is an integer because of OSP$I_AWAIT_ACTIVITY_COMPLETION.
{
{   RHFAM is always checked for a path to the destination if RHFAM is available on the
{   host system.   The NAM/VE connection request is always made because NAM/VE was
{   available for use when the translation request was made.  If NAM/VE is not
{   available for use when the connection request is made, that will be flagged.

  PROCEDURE satisfy_title_translation
    (    activity_index: integer;
         destination_list: ^nft$qtfc_destination;
         rhfam_availability: nft$network_availability;
     VAR namve_availability: nft$network_availability;
     VAR wait_list: ^ost$i_wait_list);

    CONST
      translation_wait_time = 0;

    VAR
      connection_attributes: ^nat$create_attributes,
      connection: nft$qtf_connection,
      destination: ^nft$qtfc_destination,
      directory_data: ^nat$directory_data,
      ignore_status: ost$status,
      translation_address: nat$network_address,
      translation_attributes: ^nat$translation_attributes,
      unique_name: ost$name,
      status: ost$status;

?? NEWTITLE := 'find_destination_with_req_id', EJECT ??

{ PURPOSE:
{   This procedure finds a destination that has a title translation
{   request that is the same as the given translation request.
{
{ NOTES:
{   An assumption made in this routine is that title translation
{   requests are the only activities removed from the wait list.
{   When a title translation request is added to the wait list, the
{   destination entry that spawns the title has a pointer set to that
{   activity in the wait list.

    PROCEDURE find_destination_with_req_id
      (    translation_request: nat$directory_search_identifier;
           destination_list: ^nft$qtfc_destination;
       VAR destination: ^nft$qtfc_destination);

      VAR
        destination_found: boolean;

      destination_found := FALSE;
      destination := destination_list;
      WHILE (NOT destination_found) AND (destination <> NIL) DO
        IF destination^.translation_request <> NIL THEN
          destination_found := translation_request = destination^.translation_request^.translation_request;
        IFEND;
        IF NOT destination_found THEN
          destination := destination^.link;
        IFEND;
      WHILEND;

    PROCEND find_destination_with_req_id;
?? OLDTITLE, EJECT ??
    find_destination_with_req_id (wait_list^ [activity_index].translation_request, destination_list,
          destination);

    IF destination = NIL THEN

{       INTERNAL ERROR

      nap$end_directory_search (wait_list^ [activity_index].translation_request, ignore_status);
      delete_item_from_wait_list (destination_list, activity_index, wait_list);
    ELSE
      PUSH directory_data: [[REP nac$max_directory_data_length OF cell]];
      PUSH translation_attributes: [1 .. 1];
      translation_attributes^ [1].selector := nac$translation_data;
      translation_attributes^ [1].data := directory_data;
      nap$get_title_translation (wait_list^ [activity_index].translation_request, translation_wait_time,
            translation_attributes, translation_address, status);
      IF status.normal THEN
        nap$end_directory_search (wait_list^ [activity_index].translation_request, ignore_status);
        delete_item_from_wait_list (destination_list, activity_index, wait_list);

        destination^.translation_request := NIL;

        pmp$get_unique_name (unique_name, status);
        connection.file_name := unique_name;
        connection.kind := nfc$unknown_network;

        IF rhfam_availability.available THEN
          IF destination^.next_destination_name.value = osc$null_name THEN
            request_rhfam_connection (destination^.name, connection, status);
          ELSE
            request_rhfam_connection (destination^.next_destination_name.value, connection, status);
          IFEND;
          IF NOT status.normal THEN
            amp$return (connection.file_name, ignore_status);
            connection.kind := nfc$unknown_network;
          IFEND;
        IFEND;
        IF connection.kind = nfc$unknown_network THEN
          build_namve_connect_data (translation_attributes, connection_attributes, status);
          IF status.normal THEN
            request_namve_connection (translation_address, connection_attributes, connection, status);
            IF (NOT status.normal) AND ((status.condition = nae$unknown_application) OR
                  (status.condition = nae$application_inactive) OR (status.condition = nae$network_inactive))
                  THEN
              namve_availability.available := FALSE;
              nfp$start_timer (nfc$one_minute, namve_availability.timer);
            IFEND;
          IFEND;
        IFEND;

      ELSEIF (NOT status.normal) AND (status.condition = nae$invalid_directory_search_id) THEN
        IF destination^.next_destination_name.value = osc$null_name THEN
          issue_title_translation (destination^.name, wait_list^ [activity_index].translation_request,
                namve_availability, status);
        ELSE
          issue_title_translation (destination^.next_destination_name.value, wait_list^ [activity_index].
                translation_request, namve_availability, status);
        IFEND;
      IFEND;
    IFEND;

  PROCEND satisfy_title_translation;
?? TITLE := 'select_file_for_transfer', EJECT ??

{ PURPOSE:
{   This procedure finds the next file in the destination file list
{   to be transmitted by QTFI to the remote system.
{
{ NOTES:
{   File selection on a first come, first served basis.  This file
{   selection process is being used until a better priority
{   algorithm is devised.

  PROCEDURE select_file_for_transfer
    (    destination: ^nft$qtfc_destination;
         connection: nft$qtf_connection;
         namve_host_pid: ost$name;
         rhfam_host_pid: rft$physical_identifier;
         store_forward_file_info: nft$store_forward_file_info;
     VAR transfer_file: ^nft$qtfc_file);

    VAR
      current_source_name: nft$parameter_24_definition,
      destination_name: nft$parameter_24_definition,
      file_found: boolean,
      local_status: ost$status,
      new_source_name: nft$parameter_24_definition,
      source_name_changed: boolean;

    local_status.normal := TRUE;
    file_found := FALSE;

    transfer_file := destination^.file_list;
    WHILE (NOT file_found) AND (transfer_file <> NIL) DO
      file_found := (transfer_file^.transfer_state = nfc$ready_to_transfer);
      IF file_found THEN
        IF transfer_file^.application_file_descriptor.file_kind = nfc$output_file THEN
          jmp$set_output_initiated (jmc$qtf_usage, transfer_file^.application_file_descriptor.
                output_descriptor.system_file_name, local_status);
        ELSEIF transfer_file^.application_file_descriptor.file_kind = nfc$input_file THEN
          jmp$set_input_initiated (jmc$qtf_usage, transfer_file^.application_file_descriptor.input_descriptor.
                system_job_name, local_status);
        ELSEIF transfer_file^.application_file_descriptor.file_kind = nfc$generic_file THEN
          jmp$set_qfile_initiated (nfc$qtf_namve_client_name, transfer_file^.application_file_descriptor
                .generic_descriptor.system_file_name, local_status);
        IFEND;
        file_found := local_status.normal;
        IF local_status.normal THEN
          transfer_file^.transfer_state := nfc$transfer_initiated;
          IF store_forward_file_info.file_open THEN
            destination_name.value := destination^.name;
            destination_name.size := clp$trimmed_string_size (destination_name.value);
            IF (transfer_file^.application_file_descriptor.file_kind = nfc$output_file) AND
                  (transfer_file^.application_file_descriptor.output_descriptor.source_logical_id <>
                    osc$null_name) THEN
              current_source_name.value := transfer_file^.application_file_descriptor.
                    output_descriptor.source_logical_id;
            ELSEIF (transfer_file^.application_file_descriptor.file_kind = nfc$input_file) AND
                  (transfer_file^.application_file_descriptor.input_descriptor.source_logical_id <>
                    osc$null_name) THEN
              current_source_name.value := transfer_file^.application_file_descriptor.
                    input_descriptor.source_logical_id;
            ELSEIF connection.kind = nfc$network_nam THEN
              current_source_name.value := namve_host_pid;
            ELSEIF connection.kind = nfc$network_lcn THEN
              current_source_name.value := rhfam_host_pid;
            IFEND;
            current_source_name.size := clp$trimmed_string_size (current_source_name.value);
            nfp$get_new_source_name (nfc$sf_qtf_initiator, store_forward_file_info, current_source_name,
                  destination_name, source_name_changed, new_source_name, local_status);
            IF source_name_changed THEN
              IF transfer_file^.application_file_descriptor.file_kind = nfc$output_file THEN
                transfer_file^.application_file_descriptor.output_descriptor.source_logical_id :=
                      new_source_name.value;
              ELSEIF transfer_file^.application_file_descriptor.file_kind = nfc$input_file THEN
                transfer_file^.application_file_descriptor.input_descriptor.source_logical_id :=
                      new_source_name.value;
              IFEND;
            IFEND;
            IF destination^.next_destination_name.value <> osc$null_name THEN
              IF transfer_file^.application_file_descriptor.file_kind = nfc$output_file THEN
                transfer_file^.application_file_descriptor.output_descriptor.output_destination :=
                      destination^.next_destination_name.value;
              ELSEIF transfer_file^.application_file_descriptor.file_kind = nfc$input_file THEN
                transfer_file^.application_file_descriptor.input_descriptor.job_destination_family :=
                      destination^.next_destination_name.value;
              ELSEIF transfer_file^.application_file_descriptor.file_kind = nfc$generic_file THEN
                transfer_file^.application_file_descriptor.generic_descriptor.destination :=
                      destination^.next_destination_name.value;
              IFEND;
            IFEND;
          IFEND;
        ELSE
          transfer_file^.transfer_state := nfc$wait_to_transfer;
        IFEND;
      IFEND;

{ This should be separate because file_found can change inside the previous IF statement

      IF NOT file_found THEN
        transfer_file := transfer_file^.link;
      IFEND;
    WHILEND;

  PROCEND select_file_for_transfer;
?? TITLE := 'send_file_transfer_msg_to_task', EJECT ??

{ PURPOSE:
{   This procedure will send a message to a QTFI task telling the task
{   to transfer a file over a connection, the connection file name,
{   the connection type, and the host physical identifier.

  PROCEDURE send_file_transfer_msg_to_task
    (    transfer_file: ^nft$qtfc_file;
         connection: nft$qtf_connection;
         namve_host_pid: ost$name;
         rhfam_host_pid: rft$physical_identifier;
         task_id: pmt$task_id;
     VAR last_message_sent: nft$intertask_message_kind;
     VAR transfer_start_time: nft$micro_second;
     VAR status: ost$status);

    VAR
      qtfi_task: nft$qtfi_task,
      qtfi_task_msg: nft$intertask_message,
      retry_count: nft$qtf_put_async_retry_range;

    status.normal := TRUE;
    qtfi_task_msg.kind := nfc$qtf_file_transfer;
    qtfi_task_msg.connection_kind := connection.kind;
    qtfi_task_msg.connection_file := connection.file_name;
    qtfi_task_msg.qtf_file_descriptor := transfer_file^.application_file_descriptor;

    IF qtfi_task_msg.connection_kind = nfc$network_nam THEN
      qtfi_task_msg.host_pid := namve_host_pid;
    ELSEIF qtfi_task_msg.connection_kind = nfc$network_lcn THEN
      qtfi_task_msg.host_pid := rhfam_host_pid;
    IFEND;

    retry_count := 0;

    REPEAT
      nfp$put_async_task_message (task_id, ^qtfi_task_msg, #SIZE (qtfi_task_msg), status);
      IF status.normal THEN
        last_message_sent := nfc$qtf_file_transfer;
        transfer_start_time := #FREE_RUNNING_CLOCK (0);
      ELSE
        pmp$wait (nfc$qtf_put_async_wait_time, nfc$qtf_put_async_wait_time);
        retry_count := retry_count + 1;
      IFEND;
    UNTIL (status.normal) OR (retry_count > nfc$qtf_put_async_max_retry);

  PROCEND send_file_transfer_msg_to_task;
?? TITLE := 'start_transmitting_tasks', EJECT ??

{ PURPOSE:
{   This procedure is called after acquiring queue files to start the
{   QTFI tasks to transmit files to remote destinations.  If a
{   destination entry already has a QTFI task, then the destination
{   is ignored.

  PROCEDURE start_transmitting_tasks
    (    destination_list: ^nft$qtfc_destination;
         maximum_qtfi_tasks: ost$non_negative_integers;
         namve_host_pid: ost$name;
         rhfam_host_pid: rft$physical_identifier;
         store_forward_file_info: nft$store_forward_file_info;
     VAR namve_availability: nft$network_availability;
     VAR number_of_running_qtfi_tasks: ost$non_negative_integers;
     VAR rhfam_availability: nft$network_availability;
     VAR wait_list: ^ost$i_wait_list);

    VAR
      connection: nft$qtf_connection,
      destination: ^nft$qtfc_destination,
      status: ost$status;

?? NEWTITLE := 'start_qtfi_task', EJECT ??

{ PURPOSE:
{   This procedure starts up a QTF Initiator child task to perform
{   the file transfer(s).
{
{ NOTE:
{   The file is selected before the QTFI task is started because
{   there is a possibility that there is only one file to transfer
{   but it could not be selected.  The file could have been modified
{   or terminated by the user and QTFC has not yet been notified of
{   the change in the file status.

    PROCEDURE start_qtfi_task
      (    destination: ^nft$qtfc_destination;
           namve_host_pid: ost$name;
           rhfam_host_pid: rft$physical_identifier;
           store_forward_file_info: nft$store_forward_file_info;
       VAR connection: nft$qtf_connection;
       VAR wait_list: ^ost$i_wait_list);

      CONST
        debug_async_task = FALSE;

      VAR
        current_task: ^nft$qtfi_task,
        file_complete_status: ost$status,
        ignore_status: ost$status,
        qid: pmt$queue_connection,
        task_id: pmt$task_id,
        transfer_file: ^nft$qtfc_file,
        status: ost$status;

?? NEWTITLE := 'add_qtfi_task_to_list', EJECT ??

{ PURPOSE:
{   This procedure adds a new QTFI task to the task list and will
{   add an await local queue message activity to the wait list if
{   it has not been added previously.
{
{ NOTE:
{   The procedure ADD_ITEM_TO_WAIT_LIST will only be called once
{   to add the local queue message activity.  It has to be added
{   after the first QTFI task has been started so QTFC will wait
{   for messages from all QTFI tasks that get started.  After the
{   await local queue message activity has been added to the list,
{   it should never be removed.

      PROCEDURE add_qtfi_task_to_list
        (    task_id: pmt$task_id;
             qid: pmt$queue_connection;
             destination: ^nft$qtfc_destination;
             connection: nft$qtf_connection;
         VAR wait_list: ^ost$i_wait_list;
         VAR new_task: ^nft$qtfi_task);

        VAR
          activity: ost$i_activity,
          add_local_q_msg_to_wait_list: [STATIC] boolean := TRUE;

        IF add_local_q_msg_to_wait_list THEN
          activity.activity := pmc$i_await_local_queue_message;
          activity.qid := qid;
          add_item_to_wait_list (activity, wait_list);
          add_local_q_msg_to_wait_list := FALSE;
        IFEND;

        ALLOCATE new_task;
        new_task^.back_link := NIL;
        new_task^.link := NIL;
        new_task^.file_in_transfer := NIL;
        new_task^.file_transfer_start_time := 0;
        new_task^.qid := qid;
        new_task^.task_id := task_id;
        new_task^.transfer_connection := connection;

        IF destination^.transfering_task <> NIL THEN
          destination^.transfering_task^.back_link := new_task;
          new_task^.link := destination^.transfering_task;
        IFEND;
        destination^.transfering_task := new_task;

      PROCEND add_qtfi_task_to_list;
?? OLDTITLE, EJECT ??
      select_file_for_transfer (destination, connection, namve_host_pid, rhfam_host_pid,
            store_forward_file_info, transfer_file);
      IF transfer_file <> NIL THEN
        nfp$request_asynchronous_task (nfc$qtfi_task_name, debug_async_task, task_id, qid, status);
        IF status.normal THEN
          number_of_running_qtfi_tasks := number_of_running_qtfi_tasks + 1;

          add_qtfi_task_to_list (task_id, qid, destination, connection, wait_list, current_task);

          current_task^.file_in_transfer := transfer_file;

          send_file_transfer_msg_to_task (transfer_file, connection, namve_host_pid,
                rhfam_host_pid, task_id, current_task^.last_message_sent,
                current_task^.file_transfer_start_time, status);
        ELSE
          amp$return (connection.file_name, ignore_status);
          connection.file_name := osc$null_name;
          connection.kind := nfc$unknown_network;
          transfer_file^.transfer_state := nfc$ready_to_transfer;
          IF transfer_file^.application_file_descriptor.file_kind = nfc$output_file THEN
            jmp$set_output_completed (jmc$qtf_usage, transfer_file^.name, FALSE, file_complete_status);
          ELSEIF transfer_file^.application_file_descriptor.file_kind = nfc$input_file THEN
            jmp$set_input_completed (jmc$qtf_usage, transfer_file^.name, FALSE, file_complete_status);
          ELSEIF transfer_file^.application_file_descriptor.file_kind = nfc$generic_file THEN
            jmp$set_qfile_completed (nfc$qtf_namve_client_name, transfer_file^.name, FALSE,
                  file_complete_status);
          IFEND;
        IFEND;
      IFEND;

    PROCEND  start_qtfi_task;
?? OLDTITLE, EJECT ??
    destination := destination_list;
    WHILE destination <> NIL DO
      IF (destination^.transfering_task = NIL) AND
            nfp$timer_expired (destination^.retry_timer, #FREE_RUNNING_CLOCK (0)) AND
            (destination^.file_list <> NIL) AND ((maximum_qtfi_tasks = 0) OR
            ((maximum_qtfi_tasks > 0) AND (number_of_running_qtfi_tasks < maximum_qtfi_tasks))) THEN

        establish_server_connection (destination, destination_list, rhfam_availability, namve_availability,
              connection, wait_list);
        IF connection.kind <> nfc$unknown_network THEN
          destination^.retry_timer.timer_set := FALSE;
          destination^.retry_timer.last_checked := 0;
          destination^.retry_timer.time_interval := 0;
          start_qtfi_task (destination, namve_host_pid, rhfam_host_pid, store_forward_file_info,
                connection, wait_list);
        IFEND;
      IFEND;
      destination := destination^.link;
    WHILEND;

  PROCEND start_transmitting_tasks;
?? TITLE := 'nfp$qtf_controller', EJECT ??

{ PURPOSE:
{   This procedure is the QTFC main routine.

  PROGRAM nfp$qtf_controller
    (    parameter_list: clt$parameter_list;
     VAR status: ost$status);

    VAR
      activation_date_time_qtfc: nft$bcd_time,
      destination: ^nft$qtfc_destination,
      establish_descriptor: pmt$established_handler,
      exit_condition: [STATIC, READ] pmt$condition := [pmc$block_exit_processing,
            [pmc$block_exit, pmc$program_termination, pmc$program_abort]],
      latest_time_check: nft$micro_second,
      maximum_qtfi_tasks: ost$non_negative_integers,
      namve_availability: nft$network_availability,
      namve_host_pid: ost$name,
      number_of_running_qtfi_tasks: ost$non_negative_integers,
      qtf_generic_q_password: jmt$queue_file_password,
      qtf_input_q_password: jmt$queue_file_password,
      qtf_output_q_password: jmt$queue_file_password,
      ready_index: integer,       { This has to be an integer for osp$i_await_activity_completion.
      rhfam_availability: nft$network_availability,
      rhfam_host_pid: rft$physical_identifier,
      single_transfer_per_connection: boolean,
      store_forward_file_info: nft$store_forward_file_info,
      wait_list: ^ost$i_wait_list;

?? NEWTITLE := 'exit condition handler', EJECT ??

{ PURPOSE:
{   This is a block exit condition handler.  When executed, it will
{   sign off as an RHFAM application, end all current title
{   translation requests and close, end communication with the QTFI
{   child tasks, and close and return all connection files.

    PROCEDURE exit_condition_handler
      (    condition: pmt$condition;
           condition_descriptor: ^pmt$condition_information;
           save_area: ^ost$stack_frame_save_area;
       VAR handler_status: ost$status);

      VAR
        destination: ^nft$qtfc_destination,
        ignore_status: ost$status,
        transfering_task: ^nft$qtfi_task;

      pmp$log ('Queue file Transfer Facility dropping', ignore_status);

{ End asynchronous communications with QTFI tasks.

      nfp$end_async_communication (FALSE, ignore_status);

{ End all title translations and return all connection files.

      destination := destination_list;
      WHILE destination <> NIL DO
        IF destination^.translation_request <> NIL THEN
          nap$end_directory_search (destination^.translation_request^.translation_request, ignore_status);
        IFEND;
        transfering_task := destination^.transfering_task;
        WHILE transfering_task <> NIL DO
          IF transfering_task^.transfer_connection.kind <> nfc$unknown_network THEN
            amp$return (transfering_task^.transfer_connection.file_name, ignore_status);
            IF ignore_status.normal THEN
              transfering_task^.transfer_connection.file_name := osc$null_name;
              transfering_task^.transfer_connection.kind := nfc$unknown_network;
            IFEND;
          IFEND;
          transfering_task := transfering_task^.link;
        WHILEND;
        destination := destination^.link;
      WHILEND;

      rfp$application_sign_off (nfc$qtf_rhfam_client_name, ignore_status);

      IF store_forward_file_info.file_open THEN
        nfp$close_store_forward_file (store_forward_file_info, ignore_status);
      IFEND;

      REPEAT
        clp$delete_variable (nfv$appl_def_segment_variables [nfc$appl_def_segment_for_qtf], ignore_status);
      UNTIL NOT ignore_status.normal;

    PROCEND exit_condition_handler;
?? TITLE := 'initialize qtf controller', EJECT ??

{ PURPOSE:
{   This procedure initializes values and get parameters from the
{   program call.
{
{ NOTE:
{   The host pid is a required parameter in the protocol.  The
{   parameter for the host pid is required for use with NAM/VE.
{   If RHFAM is available, QTF will use the value returned by
{   rfp$get_local_host_pid.

    PROCEDURE initialize_qtf_controller
      (    parameter_list: clt$parameter_list;
       VAR namve_host_pid: ost$name;
       VAR rhfam_host_pid: rft$physical_identifier;
       VAR qtf_generic_q_password: jmt$queue_file_password;
       VAR qtf_input_q_password: jmt$queue_file_password;
       VAR qtf_output_q_password: jmt$queue_file_password;
       VAR wait_list: ^ost$i_wait_list;
       VAR rhfam_availability: nft$network_availability;
       VAR maximum_qtfi_tasks: ost$non_negative_integers;
       VAR single_transfer_per_connection: boolean;
       VAR status: ost$status);

*copyc nft$qtf_controller_pdt

      VAR
        generic_q_registration_options: ^jmt$qfile_registration_options,
        maximum_rhf_connections: rft$application_connections,
        rhfam_status: ost$status,
        value_specified: boolean;

      status.normal := TRUE;

      jmp$register_input_application (nfc$qtf_namve_client_name, jmc$qtf_usage, qtf_input_q_password, status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;

      jmp$register_output_application (nfc$qtf_namve_client_name, jmc$qtf_usage, qtf_output_q_password,
            status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;

      PUSH generic_q_registration_options: [1..1];
      generic_q_registration_options^[1].key := jmc$notify_on_terminate;
      generic_q_registration_options^[1].notify_on_terminate := TRUE;

      jmp$register_qfile_application (nfc$qtf_namve_client_name, generic_q_registration_options,
            qtf_generic_q_password, status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;

      clp$evaluate_parameters (parameter_list, #SEQ (pdt), NIL, ^pvt, status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;

      #translate(osv$lower_to_upper, pvt [p$host_physical_identifier].value^.string_value^, namve_host_pid);

      maximum_qtfi_tasks := 0;
      IF pvt [p$maximum_qtfi_subtasks].specified THEN
          maximum_qtfi_tasks := pvt [p$maximum_qtfi_subtasks].value^.integer_value.value;
      IFEND;

      single_transfer_per_connection := pvt [p$single_transfer_per_connectio].value^.boolean_value.value;

      rhfam_availability.available := FALSE;
      rhfam_availability.timer.timer_set := FALSE;
      rhfam_availability.timer.last_checked := 0;
      rhfam_availability.timer.time_interval := 0;
      check_rhfam_sign_on (rhfam_availability, rhfam_host_pid);

      ALLOCATE qtfc_wait_list_seq: [[REP nfc$wait_list_limit OF ost$i_activity]];
      RESET qtfc_wait_list_seq;
      NEXT wait_list: [1 .. 2] IN qtfc_wait_list_seq;
      wait_list^ [1].activity := osc$i_await_unspecified_event;
      wait_list^ [2].activity := osc$i_await_time;
      wait_list^ [2].milliseconds := nfc$one_minute DIV 1000;

      nfp$create_appl_def_segment_var (nfc$appl_def_segment_for_qtf, qtfc_wait_list_seq);

    PROCEND initialize_qtf_controller;
?? OLDTITLE, EJECT ??
    status.normal := TRUE;
    number_of_running_qtfi_tasks := 0;

    get_activation_date_time (activation_date_time_qtfc);
    namve_availability.available := TRUE;   { Assumption until told otherwise.
    namve_availability.time_stamp := activation_date_time_qtfc;

    pmp$establish_condition_handler (exit_condition, ^exit_condition_handler, ^establish_descriptor, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    initialize_qtf_controller (parameter_list, namve_host_pid, rhfam_host_pid, qtf_generic_q_password,
          qtf_input_q_password, qtf_output_q_password, wait_list, rhfam_availability, maximum_qtfi_tasks,
          single_transfer_per_connection, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    nfp$open_store_forward_file (TRUE, store_forward_file_info, status);

    acquire_all_q_files (qtf_generic_q_password, qtf_input_q_password, qtf_output_q_password,
          store_forward_file_info, destination_list);
    start_transmitting_tasks (destination_list, maximum_qtfi_tasks, namve_host_pid, rhfam_host_pid,
          store_forward_file_info, namve_availability,number_of_running_qtfi_tasks,
          rhfam_availability, wait_list);

    WHILE TRUE DO
      osp$i_await_activity_completion (wait_list^, ready_index, status);
      IF status.normal THEN
        CASE wait_list^ [ready_index].activity OF
        = osc$i_await_time =
          latest_time_check := #FREE_RUNNING_CLOCK (0);
          IF (NOT rhfam_availability.available) AND (nfp$timer_expired
                (rhfam_availability.timer, latest_time_check)) THEN
            check_rhfam_sign_on (rhfam_availability, rhfam_host_pid);
          IFEND;
          IF (NOT namve_availability.available) AND (nfp$timer_expired
                (namve_availability.timer, latest_time_check)) THEN
            check_namve_client_status (destination_list, namve_availability);
          IFEND;

        = nac$i_await_title_translation =
          satisfy_title_translation (ready_index, destination_list, rhfam_availability, namve_availability,
                wait_list);

        = pmc$i_await_local_queue_message =
          process_intertask_message (destination_list, namve_host_pid, rhfam_host_pid,
                store_forward_file_info, namve_availability, number_of_running_qtfi_tasks,
                rhfam_availability, wait_list, single_transfer_per_connection);

        ELSE
          ;
        CASEND;
        acquire_all_q_files (qtf_generic_q_password, qtf_input_q_password, qtf_output_q_password,
              store_forward_file_info, destination_list);

        start_transmitting_tasks (destination_list, maximum_qtfi_tasks, namve_host_pid, rhfam_host_pid,
              store_forward_file_info, namve_availability, number_of_running_qtfi_tasks,
              rhfam_availability, wait_list);

        IF (NOT namve_availability.available) AND (UPPERBOUND (wait_list^) > 2) THEN
          remove_translation_requests (destination_list, wait_list);
        IFEND;

        destination := destination_list;
        WHILE (destination <> NIL) DO
          IF (destination^.file_list = NIL) AND (destination^.transfering_task = NIL) THEN
            delete_destination_from_list (destination, destination_list, wait_list);
            destination := destination_list;
          ELSE
            destination := destination^.link;
          IFEND;
        WHILEND;
      IFEND;
    WHILEND;

    IF store_forward_file_info.file_open THEN
      nfp$close_store_forward_file (store_forward_file_info, status);
    IFEND;

  PROCEND nfp$qtf_controller;
?? OLDTITLE ??
MODEND nfm$qtf_controller;
