?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE SCL Interpreter : Connected Files Manager' ??
MODULE clm$connected_files_manager;

{
{ PURPOSE:
{   This module contains the procedures that manage the structures which describe the "connections" between
{   files in a job.  These connections are created and deleted via the requests clp$create_file_connection
{   and clp$delete_file_connection, and the corresponding commands.  The structures are used by the file
{   access procedure (FAP) associated with a file that has been the subject of a file connection.
{

?? NEWTITLE := 'Global Declarations' ??
?? NEWTITLE := 'Connected File Structures', EJECT ??
*copyc clc$compiling_for_test_harness
*copyc clt$connected_file
?? OLDTITLE, EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc amt$local_file_name
*copyc cle$ecc_connected_file
*copyc cle$ecc_file_reference
*copyc clk$create_file_connection
*copyc clt$environment_object_contents
*copyc clt$environment_object_size
*copyc clt$env_object_pop_reason
*copyc clt$env_object_push_reason
*copyc fst$file_reference
*copyc ost$caller_identifier
*copyc ost$status
?? POP ??
*copyc pmf$job_mode
*copyc amp$get_file_attributes
*copyc bap$close_obsolete_target_files
*copyc bap$get_device_class
*copyc bap$get_path_elements
*copyc bap$verify_file_connection_attr
*copyc clp$construct_path_handle_name
*copyc clp$find_current_block
*copyc clp$find_connected_files
*copyc clp$validate_local_file_name
*copyc clv$standard_files
*copyc clv$standard_files
*copyc fmp$evaluate_path
*copyc fmp$get_device_class
*copyc fmp$get_path_elements
*copyc fmp$request_null_device
*copyc fsp$convert_fs_structure_to_pf
*copyc osp$append_status_parameter
*copyc osp$set_status_abnormal
*copyc osp$system_error
*copyc osv$task_shared_heap
*copyc pfp$get_object_information
*copyc pfp$retrieve_archived_file
?? TITLE := 'clp$eo_size_file_connections', EJECT ??

  FUNCTION [XDCL] clp$eo_size_file_connections: clt$environment_object_size;


    clp$eo_size_file_connections := #SIZE (clt$connected_files);

  FUNCEND clp$eo_size_file_connections;
?? TITLE := 'clp$eo_init_file_connections', EJECT ??

  PROCEDURE [XDCL] clp$eo_init_file_connections
    (    object: ^clt$environment_object_contents);

    VAR
      connected_files: ^clt$connected_files;


    connected_files := object;

    connected_files^.subject_tree := NIL;
    connected_files^.echo_count := 0;
    connected_files^.connection_level := 0;

  PROCEND clp$eo_init_file_connections;
?? TITLE := 'clp$eo_push_file_connections', EJECT ??

  PROCEDURE [XDCL] clp$eo_push_file_connections
    (    push_reason: clt$env_object_push_reason;
         new_object: ^clt$environment_object_contents;
         new_object_in_current_task: boolean;
         pushed_object_in_current_task: boolean;
         pushed_object: ^clt$environment_object_contents;
     VAR status: ost$status);

    VAR
      new_connected_files: ^clt$connected_files,
      old_connected_files: ^clt$connected_files;

?? NEWTITLE := 'copy_subject_node', EJECT ??

    PROCEDURE copy_subject_node
      (    old_subject_node: clt$connected_file_subject;
       VAR new_subject_node: ^clt$connected_file_subject);

      VAR
        i: clt$connected_file_target_index,
        j: 0 .. clc$max_connected_file_targets;


      ALLOCATE new_subject_node IN osv$task_shared_heap^;
      new_subject_node^.path_handle_name := old_subject_node.path_handle_name;
      new_subject_node^.left_link := NIL;
      new_subject_node^.right_link := NIL;
      new_subject_node^.connection_level := old_subject_node.connection_level;

      IF old_subject_node.targets = NIL THEN
        new_subject_node^.targets := NIL;
      ELSE
        ALLOCATE new_subject_node^.targets: [1 .. UPPERBOUND (old_subject_node.targets^)] IN
              osv$task_shared_heap^;

        j := UPPERBOUND (new_subject_node^.targets^);
        FOR i := UPPERBOUND (old_subject_node.targets^) DOWNTO 1 DO
          IF old_subject_node.targets^ [i].connection_active THEN
            new_subject_node^.targets^ [j] := old_subject_node.targets^ [i];
            j := j - 1;
          IFEND;
        FOREND;

        FOR i := j DOWNTO 1 DO
          new_subject_node^.targets^ [i].connection_active := FALSE;
          new_subject_node^.targets^ [i].path_handle_name := osc$null_name;
          new_subject_node^.targets^ [i].connection_level := 0;
        FOREND;
      IFEND;

      IF old_subject_node.left_link = NIL THEN
        new_subject_node^.left_link := NIL;
      ELSE
        copy_subject_node (old_subject_node.left_link^, new_subject_node^.left_link);
      IFEND;

      IF old_subject_node.right_link = NIL THEN
        new_subject_node^.right_link := NIL;
      ELSE
        copy_subject_node (old_subject_node.right_link^, new_subject_node^.right_link);
      IFEND;

    PROCEND copy_subject_node;
?? OLDTITLE, EJECT ??

    status.normal := TRUE;

    new_connected_files := new_object;
    old_connected_files := pushed_object;

    IF old_connected_files^.subject_tree = NIL THEN
      new_connected_files^.subject_tree := NIL;
    ELSE
      copy_subject_node (old_connected_files^.subject_tree^, new_connected_files^.subject_tree);
    IFEND;

    new_connected_files^.echo_count := old_connected_files^.echo_count;
    new_connected_files^.connection_level := old_connected_files^.connection_level;

  PROCEND clp$eo_push_file_connections;
?? TITLE := 'clp$eo_pop_file_connections', EJECT ??

  PROCEDURE [XDCL] clp$eo_pop_file_connections
    (    pop_reason: clt$env_object_pop_reason;
         object: ^clt$environment_object_contents;
         object_in_current_task: boolean;
         pushed_object_in_current_task: boolean;
         pushed_object: ^clt$environment_object_contents;
     VAR status: ost$status);

    VAR
      connected_files: ^clt$connected_files,
      current_connection_level: clt$file_connection_level,
      subject_tree: ^clt$connected_file_subject;


    status.normal := TRUE;

    connected_files := object;

    IF (pop_reason = clc$eo_pop_requested) OR (pop_reason = clc$eo_pop_for_block) THEN

{ Close any obsolete target files in the environment being popped.

      bap$close_obsolete_target_files (connected_files);
    IFEND;

{ Pop the current connected_files environment.

    current_connection_level := connected_files^.connection_level;

    IF connected_files^.subject_tree <> NIL THEN
      subject_tree := connected_files^.subject_tree;
      release_subject_node (subject_tree);
    IFEND;

{ Done if cleaning up or popping for an asynchronous task.

    IF pushed_object = NIL THEN
      RETURN;
    IFEND;

{ Update "global" connection_level.

    connected_files := pushed_object;
    connected_files^.connection_level := current_connection_level;

    IF (pop_reason = clc$eo_pop_requested) OR (pop_reason = clc$eo_pop_for_block) THEN

{ Close targets that were created in the popped environment and are now obsolete.

      bap$close_obsolete_target_files (connected_files);
    IFEND;

  PROCEND clp$eo_pop_file_connections;
?? TITLE := 'clp$find_connected_file', EJECT ??

  PROCEDURE [XDCL, #GATE] clp$find_connected_file
    (    local_file_name: amt$local_file_name;
     VAR connected_file_subject: ^clt$connected_file_subject);

    VAR
      connected_files: ^clt$connected_files,
      previous_node: ^^clt$connected_file_subject;

    clp$find_connected_files (connected_files);

    search_subject_tree (local_file_name, ^connected_files^.subject_tree, previous_node,
          connected_file_subject);

  PROCEND clp$find_connected_file;
?? TITLE := 'clp$return_connected_file', EJECT ??

  PROCEDURE [XDCL] clp$return_connected_file
    (    local_file_name: amt$local_file_name);

    VAR
      connected_files: ^clt$connected_files,
      previous_node: ^^clt$connected_file_subject,
      subject_node: ^clt$connected_file_subject;

    clp$find_connected_files (connected_files);

    search_subject_tree (local_file_name, ^connected_files^.subject_tree, previous_node, subject_node);

    IF subject_node <> NIL THEN
      delete_subject_node (previous_node, subject_node);
    IFEND;

  PROCEND clp$return_connected_file;
?? TITLE := 'release_subject_node', EJECT ??

  PROCEDURE release_subject_node
    (VAR subject_node {input} : ^clt$connected_file_subject);

    IF subject_node^.left_link <> NIL THEN
      release_subject_node (subject_node^.left_link);
    IFEND;

    IF subject_node^.right_link <> NIL THEN
      release_subject_node (subject_node^.right_link);
    IFEND;

    IF subject_node^.targets <> NIL THEN
      FREE subject_node^.targets IN osv$task_shared_heap^;
    IFEND;

    FREE subject_node IN osv$task_shared_heap^;

  PROCEND release_subject_node;
?? TITLE := 'search_subject_tree', EJECT ??

  PROCEDURE [INLINE] search_subject_tree
    (    path_handle_name: fst$path_handle_name;
         subject_tree: ^^clt$connected_file_subject;
     VAR previous_node: ^^clt$connected_file_subject;
     VAR subject_node: ^clt$connected_file_subject);

    previous_node := subject_tree;
    WHILE previous_node^ <> NIL DO
      subject_node := previous_node^;
      IF path_handle_name = subject_node^.path_handle_name THEN
        RETURN;
      ELSEIF path_handle_name < subject_node^.path_handle_name THEN
        previous_node := ^subject_node^.left_link;
      ELSE {path_handle_name > subject_node^.path_handle_name
        previous_node := ^subject_node^.right_link;
      IFEND;
    WHILEND;
    subject_node := NIL;

  PROCEND search_subject_tree;
?? TITLE := 'delete_subject_node', EJECT ??

  PROCEDURE [INLINE] delete_subject_node
    (    previous_node: ^^clt$connected_file_subject;
         subject_node: ^clt$connected_file_subject);

    VAR
      current_node: ^clt$connected_file_subject,
      left_subtree: ^clt$connected_file_subject,
      previous_right_node: ^clt$connected_file_subject,
      right_node: ^clt$connected_file_subject;

    IF subject_node^.left_link = NIL THEN
      previous_node^ := subject_node^.right_link;
    ELSEIF subject_node^.right_link = NIL THEN
      previous_node^ := subject_node^.left_link;
    ELSE
      left_subtree := subject_node^.left_link;
      IF left_subtree^.right_link = NIL THEN
        left_subtree^.right_link := subject_node^.right_link;
        previous_node^ := left_subtree;
      ELSE
        right_node := left_subtree;
        REPEAT
          previous_right_node := right_node;
          right_node := right_node^.right_link;
        UNTIL right_node^.right_link = NIL;
        previous_right_node^.right_link := right_node^.left_link;
        right_node^.left_link := left_subtree;
        right_node^.right_link := subject_node^.right_link;
        previous_node^ := right_node;
      IFEND;
    IFEND;

    IF subject_node^.targets <> NIL THEN
      FREE subject_node^.targets IN osv$task_shared_heap^;
    IFEND;
    current_node := subject_node;
    FREE current_node IN osv$task_shared_heap^;

  PROCEND delete_subject_node;
?? TITLE := 'clp$get_ultimate_connection', EJECT ??
*copyc clh$get_ultimate_connection

  PROCEDURE [XDCL, #GATE] clp$get_ultimate_connection
    (    candidate_name: fst$path_handle_name;
     VAR ultimate_name: fst$path_handle_name;
     VAR status: ost$status);

    VAR
      connected_files: ^clt$connected_files,
      current_name: fst$path_handle_name,
      evaluated_file_reference: fst$evaluated_file_reference,
      ignore_cycle_description: ^fmt$cycle_description,
      ignore_previous_node: ^^clt$connected_file_subject,
      i: clt$connected_file_target_index,
      subject_file: ^clt$connected_file_subject;

    status.normal := TRUE;

    clp$find_connected_files (connected_files);
    fmp$evaluate_path (candidate_name, $bat$process_pt_work_list [bac$resolve_path],
          evaluated_file_reference, ignore_cycle_description, status);
    IF NOT status.normal THEN
      RETURN;
    ELSEIF (NOT evaluated_file_reference.path_handle_info.path_handle_present) THEN
      ultimate_name := candidate_name;
      RETURN;
    IFEND;
    clp$construct_path_handle_name (evaluated_file_reference.path_handle_info.path_handle, current_name);

  /get_ultimate_connection/
    WHILE TRUE DO
      ultimate_name := current_name;
      search_subject_tree (current_name, ^connected_files^.subject_tree, ignore_previous_node, subject_file);
      IF (subject_file = NIL) OR (subject_file^.targets = NIL) THEN
        EXIT /get_ultimate_connection/;
      IFEND;
      FOR i := UPPERBOUND (subject_file^.targets^) DOWNTO 1 DO
        IF subject_file^.targets^ [i].connection_active THEN
          current_name := subject_file^.targets^ [i].path_handle_name;
          CYCLE /get_ultimate_connection/;
        IFEND;
      FOREND;
      EXIT /get_ultimate_connection/;
    WHILEND /get_ultimate_connection/;

    IF ultimate_name = clv$standard_files [clc$sf_null_file].path_handle_name THEN
      ultimate_name := candidate_name;
    IFEND;

  PROCEND clp$get_ultimate_connection;
?? TITLE := 'clp$internal_cre_file_connect', EJECT ??

  PROCEDURE [XDCL, #GATE] clp$internal_cre_file_connect
    (    subject_file: fst$path_handle_name;
         target_file: fst$path_handle_name;
         target_open_position: fst$open_position;
     VAR status: ost$status);

    VAR
      caller_id: ost$caller_identifier,
      catalog_depth: fst$catalog_depth,
      device_assigned: boolean,
      device_class: rmt$device_class,
      evaluated_file_reference: fst$evaluated_file_reference,
      file_path: ^pft$path,
      local_status: ost$status,
      subject_file_handle: fst$path_handle_name,
      subject_path_handle: fmt$path_handle,
      target_file_handle: fst$path_handle_name,
      path_handle: fmt$path_handle,
      name_is_path_handle: boolean,
      name_is_valid: boolean,
      subject_attributes: array [1 .. 2] of amt$get_item,
      target_attributes: array [1 .. 3] of amt$get_item,
      ignore_local_file: boolean,
      ignore_existing_file: boolean,
      ignore_contains_data: boolean,
      information_request: fst$goi_information_request,
      new_subject_file: boolean,
      object_info: ^fst$goi_object_information,
      object_info_sequence: ^SEQ (*),
      object_info_sequence_size: ost$positive_integers,
      i: clt$connected_file_target_index,
      circular_connection: boolean,
      connected_files: ^clt$connected_files,
      connected_file: ^clt$connected_file_subject,
      previous_connected_file: ^^clt$connected_file_subject,
      new_target: ^clt$connected_file_target;

?? NEWTITLE := 'check_for_circular_connection', EJECT ??

    PROCEDURE check_for_circular_connection
      (    candidate_name: fst$path_handle_name;
       VAR circular_connection: boolean);

      VAR
        i: clt$connected_file_target_index,
        ignore_previous_node: ^^clt$connected_file_subject,
        subject_file: ^clt$connected_file_subject;

      circular_connection := FALSE;
      search_subject_tree (candidate_name, ^connected_files^.subject_tree, ignore_previous_node,
            subject_file);
      IF (subject_file = NIL) OR (subject_file^.targets = NIL) THEN
        RETURN;
      IFEND;
      FOR i := 1 TO UPPERBOUND (subject_file^.targets^) DO
        IF subject_file^.targets^ [i].connection_active THEN
          IF subject_file_handle = subject_file^.targets^ [i].path_handle_name THEN
            circular_connection := TRUE;
            RETURN;
          IFEND;
          check_for_circular_connection (subject_file^.targets^ [i].path_handle_name, circular_connection);
          IF circular_connection THEN
            RETURN;
          IFEND;
        IFEND;
      FOREND;

    PROCEND check_for_circular_connection;
?? TITLE := 'check_for_redundant_connection', EJECT ??

    PROCEDURE [INLINE] check_for_redundant_connection
      (VAR targets {input} : clt$connected_file_targets);

      VAR
        i: clt$connected_file_target_index;

      FOR i := 1 TO UPPERBOUND (targets) DO
        IF targets [i].connection_active AND (targets [i].path_handle_name = target_file_handle) THEN

          osp$set_status_abnormal ('CL', cle$duplicate_file_connection, subject_file_handle, local_status);
          osp$append_status_parameter (osc$status_parameter_delimiter, target_file_handle, local_status);
          RETURN;

        IFEND;
      FOREND;

    PROCEND check_for_redundant_connection;
?? TITLE := 'create_new_targets_array', EJECT ??

    PROCEDURE [INLINE] create_new_targets_array
      (VAR old_targets {input, output} : ^clt$connected_file_targets);

      VAR
        old_target_count: 0 .. clc$max_connected_file_targets,
        new_targets: ^clt$connected_file_targets,
        i: clt$connected_file_target_index;

      IF old_targets = NIL THEN
        old_target_count := 0;
      ELSE
        old_target_count := UPPERBOUND (old_targets^);
      IFEND;

      ALLOCATE new_targets: [1 .. clc$min_connected_file_targets + old_target_count] IN osv$task_shared_heap^;
      FOR i := UPPERBOUND (new_targets^) DOWNTO clc$min_connected_file_targets + 1 DO
        new_targets^ [i] := old_targets^ [i - clc$min_connected_file_targets];
      FOREND;
      FOR i := 1 TO clc$min_connected_file_targets DO
        new_targets^ [i].connection_active := FALSE;
        new_targets^ [i].path_handle_name := osc$null_name;
        new_targets^ [i].connection_level := 0;
      FOREND;

      IF old_targets <> NIL THEN
        FREE old_targets IN osv$task_shared_heap^;
      IFEND;

      old_targets := new_targets;

    PROCEND create_new_targets_array;
?? OLDTITLE, EJECT ??

    #CALLER_ID (caller_id);
    status.normal := TRUE;
    local_status.normal := TRUE;

  /create_connection/
    BEGIN

      clp$validate_local_file_name (subject_file, subject_file_handle, subject_path_handle,
            name_is_path_handle, name_is_valid);
      IF NOT name_is_path_handle THEN
        osp$set_status_abnormal ('CL', cle$improper_subject_file_name, subject_file, local_status);
        EXIT /create_connection/;
      IFEND;
      IF target_file = osc$null_name THEN
        target_file_handle := osc$null_name;
      ELSE
        clp$validate_local_file_name (target_file, target_file_handle, path_handle, name_is_path_handle,
              name_is_valid);
        IF NOT name_is_path_handle THEN
          osp$set_status_abnormal ('CL', cle$improper_target_file_name, target_file, local_status);
          EXIT /create_connection/;
        IFEND;
      IFEND;

      clp$find_connected_files (connected_files);

      IF subject_file_handle = target_file_handle THEN
        circular_connection := TRUE;
      ELSE
        check_for_circular_connection (target_file_handle, circular_connection);
      IFEND;
      IF circular_connection THEN
        osp$set_status_abnormal ('CL', cle$circular_file_connection, subject_file, local_status);
        osp$append_status_parameter (osc$status_parameter_delimiter, target_file, local_status);
        EXIT /create_connection/;
      IFEND;

      IF target_file_handle <> osc$null_name THEN
        subject_attributes [1].key := amc$file_contents;
        subject_attributes [2].key := amc$file_structure;
        amp$get_file_attributes (subject_file_handle, subject_attributes, ignore_local_file,
              ignore_existing_file, ignore_contains_data, local_status);
        IF NOT local_status.normal THEN
          EXIT /create_connection/;
        IFEND;
        target_attributes [1].key := amc$file_contents;
        target_attributes [2].key := amc$file_structure;
        target_attributes [3].key := amc$permanent_file;
        amp$get_file_attributes (target_file_handle, target_attributes, ignore_local_file,
              ignore_existing_file, ignore_contains_data, local_status);
        IF NOT local_status.normal THEN
          EXIT /create_connection/;
        IFEND;
        IF target_attributes[3].permanent_file THEN
          fmp$get_path_elements (path_handle, evaluated_file_reference, local_status);
          PUSH file_path: [1 .. evaluated_file_reference.number_of_path_elements];
          fsp$convert_fs_structure_to_pf (evaluated_file_reference, file_path);
          IF local_status.normal THEN
            catalog_depth.depth_specification := fsc$specific_depth;
            catalog_depth.depth := 1;
            information_request.catalog_depth := catalog_depth;
            information_request.object_information_requests := $fst$goi_object_info_requests
                  [fsc$goi_cycle_device_info];
            object_info_sequence_size := #SIZE (fst$goi_object_information) + fsc$max_path_size +
                  #SIZE (fst$goi_object) + #SIZE (fst$device_information);
            PUSH object_info_sequence: [[REP object_info_sequence_size OF cell]];
            pfp$get_object_information (target_file_handle, information_request,
                  NIL, object_info_sequence, local_status);
            IF local_status.normal THEN
              RESET object_info_sequence;
              NEXT object_info IN object_info_sequence;
              IF (object_info^.object^.cycle_device_information^.
                    mass_storage_device_info.object_condition = fsc$data_retrieval_required) THEN
                 pfp$retrieve_archived_file (file_path^, object_info^.object^.cycle_number,
                       osc$null_name, osc$wait, local_status);
              IFEND;
            IFEND;
          IFEND;
        IFEND;
        IF NOT local_status.normal THEN
          EXIT /create_connection/;
        IFEND;
        bap$verify_file_connection_attr (FALSE, subject_file_handle, target_file_handle,
              subject_attributes [1].file_contents, target_attributes [1].
              file_contents, subject_attributes [2].file_structure, target_attributes [2].file_structure,
              local_status);
        IF NOT local_status.normal THEN
          EXIT /create_connection/;
        IFEND;
      IFEND;

      search_subject_tree (subject_file_handle, ^connected_files^.subject_tree, previous_connected_file,
            connected_file);

      new_subject_file := connected_file = NIL;
      IF new_subject_file THEN

{ Assign subject file to null device.

        ?IF clc$compiling_for_test_harness THEN
          bap$get_path_elements (subject_path_handle, evaluated_file_reference, local_status);
        ?ELSE
          fmp$get_path_elements (subject_path_handle, evaluated_file_reference, local_status);
        ?IFEND
        IF NOT local_status.normal THEN
          EXIT /create_connection/;
        IFEND;

        ?IF clc$compiling_for_test_harness THEN
          bap$get_device_class (subject_path_handle, device_assigned, device_class, local_status);
        ?ELSE
          fmp$get_device_class (subject_path_handle, device_assigned, device_class, local_status);
        ?IFEND
        IF NOT local_status.normal THEN
          device_class := rmc$null_device;
          device_assigned := FALSE;
          local_status.normal := TRUE;
        IFEND;

        IF device_class <> rmc$connected_file_device THEN
          IF device_assigned THEN
            osp$set_status_abnormal ('CL', cle$subject_cannot_be_connected, subject_file_handle,
                  local_status);
            EXIT /create_connection/;
          IFEND;

          fmp$request_null_device (rmc$connected_file_device, evaluated_file_reference, local_status);
          IF NOT local_status.normal THEN
            EXIT /create_connection/;
          IFEND;
        IFEND;

{ Create descriptor for subject file.

        ALLOCATE connected_file IN osv$task_shared_heap^;
        connected_file^.path_handle_name := subject_file_handle;
        connected_file^.left_link := NIL;
        connected_file^.right_link := NIL;
        connected_file^.connection_level := 0;
        connected_file^.targets := NIL;

      ELSEIF (connected_file^.targets <> NIL) AND (target_file_handle <> osc$null_name) THEN

        check_for_redundant_connection (connected_file^.targets^);
        IF NOT local_status.normal THEN
          EXIT /create_connection/;
        IFEND;
      IFEND;

      IF target_file_handle <> osc$null_name THEN

{ Create descriptor for target file.

        new_target := NIL;
        IF connected_file^.targets <> NIL THEN

        /find_entry_for_target/
          FOR i := 1 TO UPPERBOUND (connected_file^.targets^) DO
            IF connected_file^.targets^ [i].connection_active THEN
              IF i > 1 THEN
                new_target := ^connected_file^.targets^ [i - 1];
              IFEND;
              EXIT /find_entry_for_target/;
            ELSEIF i = UPPERBOUND (connected_file^.targets^) THEN
              new_target := ^connected_file^.targets^ [i];
              EXIT /find_entry_for_target/;
            IFEND;
          FOREND /find_entry_for_target/;
        IFEND;

        IF new_target = NIL THEN
          create_new_targets_array (connected_file^.targets);
          new_target := ^connected_file^.targets^ [clc$min_connected_file_targets];
        IFEND;

{ Activate connection.

        new_target^.connection_active := TRUE;
        new_target^.path_handle_name := target_file_handle;
        new_target^.open_position := target_open_position;
        new_target^.connection_ring := caller_id.ring;

{  Increment global connection_level.

        connected_files^.connection_level := connected_files^.connection_level + 1;

{  Update subject's and target's connection levels to match new global value.

        connected_file^.connection_level := connected_files^.connection_level;
        new_target^.connection_level := connected_file^.connection_level;
      IFEND;

      IF new_subject_file THEN

{ Link up new subject file.

        previous_connected_file^ := connected_file;
      IFEND;
    END /create_connection/;

    IF local_status.normal AND (subject_file_handle = clv$standard_files [clc$sf_echo_file].
          path_handle_name) AND (target_file_handle <> osc$null_name) AND
          (target_file_handle <> clv$standard_files [clc$sf_null_file].path_handle_name) THEN
      connected_files^.echo_count := connected_files^.echo_count + 1;
    IFEND;

    IF NOT local_status.normal THEN
      status := local_status;
    IFEND;

  PROCEND clp$internal_cre_file_connect;
?? TITLE := 'clp$internal_del_file_connect', EJECT ??

  PROCEDURE [XDCL, #GATE] clp$internal_del_file_connect
    (    subject_file: fst$path_handle_name;
         target_file: fst$path_handle_name;
     VAR status: ost$status);

    VAR
      caller_id: ost$caller_identifier,
      local_status: ost$status,
      subject_file_handle: fst$path_handle_name,
      target_file_handle: fst$path_handle_name,
      path_handle: fmt$path_handle,
      name_is_path_handle: boolean,
      name_is_valid: boolean,
      connected_files: ^clt$connected_files,
      previous_connected_file: ^^clt$connected_file_subject,
      connected_file: ^clt$connected_file_subject,
      connected_file_target: ^clt$connected_file_target,
      target_index: clt$connected_file_target_index;

    #CALLER_ID (caller_id);
    status.normal := TRUE;
    local_status.normal := TRUE;

  /delete_connection/
    BEGIN

      clp$validate_local_file_name (subject_file, subject_file_handle, path_handle, name_is_path_handle,
            name_is_valid);
      IF NOT name_is_path_handle THEN
        osp$set_status_abnormal ('CL', cle$improper_subject_file_name, subject_file, local_status);
        EXIT /delete_connection/;
      IFEND;
      clp$validate_local_file_name (target_file, target_file_handle, path_handle, name_is_path_handle,
            name_is_valid);
      IF NOT name_is_path_handle THEN
        osp$set_status_abnormal ('CL', cle$improper_target_file_name, target_file_handle, local_status);
        EXIT /delete_connection/;
      IFEND;
      IF subject_file_handle = target_file_handle THEN
        osp$set_status_abnormal ('CL', cle$unknown_file_connection, subject_file_handle, local_status);
        osp$append_status_parameter (osc$status_parameter_delimiter, target_file_handle, local_status);
        EXIT /delete_connection/;
      IFEND;

      clp$find_connected_files (connected_files);

      search_subject_tree (subject_file_handle, ^connected_files^.subject_tree, previous_connected_file,
            connected_file);

    /find_target/
      BEGIN
        connected_file_target := NIL;
        IF (connected_file <> NIL) AND (connected_file^.targets <> NIL) THEN
          FOR target_index := 1 TO UPPERBOUND (connected_file^.targets^) DO
            connected_file_target := ^connected_file^.targets^ [target_index];
            IF connected_file_target^.connection_active AND (connected_file_target^.path_handle_name =
                  target_file_handle) THEN
              EXIT /find_target/;
            IFEND;
          FOREND;
          connected_file_target := NIL;
        IFEND;
      END /find_target/;

      IF (connected_file = NIL) OR (connected_file_target = NIL) THEN
        osp$set_status_abnormal ('CL', cle$unknown_file_connection, subject_file_handle, local_status);
        osp$append_status_parameter (osc$status_parameter_delimiter, target_file_handle, local_status);
        EXIT /delete_connection/;
      IFEND;

      IF caller_id.ring > connected_file_target^.connection_ring THEN
        osp$set_status_abnormal ('CL', cle$connection_cannot_be_broken, subject_file_handle, local_status);
        osp$append_status_parameter (osc$status_parameter_delimiter, target_file_handle, local_status);
        EXIT /delete_connection/;
      IFEND;

      IF subject_file_handle = clv$standard_files [clc$sf_response_file].path_handle_name THEN

      /check_original_response_connect/
        BEGIN

        /ok/
          BEGIN
            IF target_file_handle = clv$standard_files [clc$sf_job_log_file].path_handle_name THEN
              EXIT /ok/;
            ELSEIF target_file_handle = clv$standard_files [clc$sf_job_output_file].path_handle_name THEN
              IF pmf$job_mode () <> jmc$batch THEN
                EXIT /ok/;
              IFEND;
            IFEND;
            EXIT /check_original_response_connect/;
          END /ok/;

          osp$set_status_abnormal ('CL', cle$connection_cannot_be_broken, subject_file_handle, local_status);
          osp$append_status_parameter (osc$status_parameter_delimiter, target_file_handle, local_status);
          EXIT /delete_connection/;
        END /check_original_response_connect/;
      IFEND;

{ Deactivate the connection.

      FOR target_index := target_index DOWNTO 2 DO
        connected_file^.targets^ [target_index] := connected_file^.targets^ [target_index - 1];
      FOREND;
      connected_file^.targets^ [1].connection_active := FALSE;
      connected_file^.targets^ [1].path_handle_name := osc$null_name;

{ Increment global connection_level and use it to update connection_level of subject and
{ target.

      connected_files^.connection_level := connected_files^.connection_level + 1;
      connected_file^.connection_level := connected_files^.connection_level;
      connected_file^.targets^ [1].connection_level := connected_file^.connection_level;

    END /delete_connection/;

    IF local_status.normal AND (subject_file_handle = clv$standard_files [clc$sf_echo_file].
          path_handle_name) AND (target_file_handle <> clv$standard_files [clc$sf_null_file].path_handle_name)
          THEN
      connected_files^.echo_count := connected_files^.echo_count - 1;
    IFEND;

    IF NOT local_status.normal THEN
      status := local_status;
    IFEND;

  PROCEND clp$internal_del_file_connect;
?? TITLE := 'clp$internal_del_all_targets', EJECT ??

  PROCEDURE [XDCL, #GATE] clp$internal_del_all_targets
    (    subject_file: fst$path_handle_name;
     VAR status: ost$status);

    VAR
      connected_file: ^clt$connected_file_subject,
      connected_file_targets: ^clt$connected_file_targets,
      local_status: ost$status,
      target_index: clt$connected_file_target_index;


    status.normal := TRUE;
    clp$find_connected_file (subject_file, connected_file);
    IF (connected_file = NIL) OR (connected_file^.targets = NIL) THEN
      RETURN;
    IFEND;
    PUSH connected_file_targets: [1 .. UPPERBOUND (connected_file^.targets^)];
    IF connected_file_targets = NIL THEN
      RETURN;
    IFEND;
    connected_file_targets^ := connected_file^.targets^;

    FOR target_index := 1 TO UPPERBOUND (connected_file_targets^) DO
      IF connected_file_targets^ [target_index].connection_active THEN
        clp$internal_del_file_connect (subject_file, connected_file_targets^ [target_index].path_handle_name,
              local_status);
        IF (NOT local_status.normal) AND (status.normal) AND
              (status.condition <> cle$connection_cannot_be_broken) THEN
          status := local_status;
        IFEND;
      IFEND;
    FOREND;

  PROCEND clp$internal_del_all_targets;
?? TITLE := 'clp$update_connected_files', EJECT ??

  PROCEDURE [XDCL] clp$update_connected_files
    (    new_ring: ost$valid_ring );

    VAR
      connected_files: ^clt$connected_files;

?? NEWTITLE := 'update_subject_node', EJECT ??

    PROCEDURE update_subject_node
      (subject_node: ^clt$connected_file_subject);

      VAR
        i: clt$connected_file_target_index;


      IF subject_node^.left_link <> NIL THEN
        update_subject_node (subject_node^.left_link);
      IFEND;

      IF subject_node^.right_link <> NIL THEN
        update_subject_node (subject_node^.right_link);
      IFEND;

      IF subject_node^.targets <> NIL THEN
        FOR i := UPPERBOUND (subject_node^.targets^) DOWNTO 1 DO
          IF subject_node^.targets^ [i].connection_active THEN
            subject_node^.targets^ [i].connection_ring := new_ring;
          IFEND;
        FOREND;
      IFEND;

    PROCEND update_subject_node;
?? OLDTITLE, EJECT ??

    clp$find_connected_files (connected_files);

    IF connected_files^.subject_tree <> NIL THEN
      update_subject_node (connected_files^.subject_tree);
    IFEND;

  PROCEND clp$update_connected_files;

MODEND clm$connected_files_manager;
