?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE : nfm$store_forward_utilities' ??
MODULE nfm$store_forward_utilities;

{ PURPOSE:
{   This module is a collection of utility procedures to access
{   and process the System's Store/Forward Network FIle.
{

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc nfe$manage_store_forward_netwrk
*copyc nft$sf_application_name_info
*copyc nft$sf_group_name_information
*copyc nft$sf_source_name_information
*copyc nft$sf_target_name_information
*copyc nft$store_forward_file_info
*copyc nft$store_forward_file_pointers
*copyc osd$integer_limits
?? POP ??
*copyc amp$get_segment_pointer
*copyc amp$return
*copyc avp$get_capability
*copyc fsp$close_file
*copyc fsp$open_file
*copyc jmp$system_job
*copyc osp$set_status_abnormal
*copyc pfp$attach
*copyc pmp$disestablish_cond_handler
*copyc pmp$establish_condition_handler
*copyc pmp$get_unique_name
*copyc pmp$log
*copyc pmp$wait
?? OLDTITLE ??
?? NEWTITLE := 'Global Variables Referenced by This Module', EJECT ??
*copyc nfc$manage_store_forward_file
*copyc nfv$manage_sf_network
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nfp$open_store_forward_file', EJECT ??

*copyc nfh$open_store_forward_file

  PROCEDURE [XDCL] nfp$open_store_forward_file
    (    attach_file: boolean;
     VAR store_forward_file_info: nft$store_forward_file_info;
     VAR status: ost$status);

    CONST
      delay_time_for_attach = 15 * 1000, { 15 seconds }
      max_attach_tries = 10;

    VAR
      attach_count: 0 .. max_attach_tries,
      establish_descriptor: pmt$established_handler,
      exit_condition: [STATIC, READ] pmt$condition := [pmc$condition_combination,
            [pmc$block_exit_processing, mmc$segment_access_condition]],
      ignore_status: ost$status,
      pf_access_mode: pft$usage_selections,
      pf_cycle_selection: pft$cycle_selector,
      pf_password: pft$password,
      pf_share_mode: pft$share_selections,
      pf_store_forward_file_path: ^pft$path,
      ptr_file_attachment_options: ^fst$attachment_options,
      ptr_store_forward_file_ptrs: ^nft$store_forward_file_pointers,
      read_error: boolean,
      user_capability_network_appl: boolean,
      user_capability_network_oper: boolean;

?? NEWTITLE := 'attach_sf_file_cond_handler', EJECT ??

{ PURPOSE:
{   This is the condition handler for the procedure READ_STORE_FORWARD_FILE.

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

      VAR
        ignore_status: ost$status;

      pmp$log (' Errors encountered while attaching the Systems Store/Forward Network File', ignore_status);
      fsp$close_file (store_forward_file_info.file_identifier, ignore_status);
      store_forward_file_info.file_open := FALSE;
      IF read_error THEN
        osp$set_status_abnormal (nfc$status_id, nfe$sf_read_network_file_error, ' ', status);
      IFEND;
      amp$return (nfc$manage_store_forward_file, ignore_status);
    PROCEND attach_sf_file_cond_handler;
?? OLDTITLE, EJECT ??
    status.normal := TRUE;
    store_forward_file_info.file_open := FALSE;
    store_forward_file_info.pointers.ptr_application_name_list := NIL;
    store_forward_file_info.pointers.ptr_group_name_list := NIL;
    store_forward_file_info.pointers.ptr_source_name_list := NIL;
    store_forward_file_info.pointers.ptr_target_name_list := NIL;
    read_error := FALSE;

{ check to see if the user is a system job.  if the user is not a system job, then
{ check to see if the user has validation for either network_application_management or network
{ operation.  If the user does not have validation, return with caller not privileged error.

    IF NOT jmp$system_job () THEN
      avp$get_capability (avc$network_applic_management, avc$user, user_capability_network_appl, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;
      avp$get_capability (avc$network_operation, avc$user, user_capability_network_oper, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;
      IF (NOT user_capability_network_appl) AND (NOT user_capability_network_oper) THEN
        osp$set_status_abnormal (nfc$status_id, nfe$sf_caller_not_privileged, nfv$manage_sf_network, status);
        RETURN;
      IFEND;
    IFEND;

    IF attach_file THEN

{ if requested by the user, attach the store_forward_network file.

      PUSH pf_store_forward_file_path: [1 .. 4];
      pf_store_forward_file_path^ [1] := nfc$sf_family_name;
      pf_store_forward_file_path^ [2] := nfc$sf_user_name;
      pf_store_forward_file_path^ [3] := nfc$sf_subcatalog_name;
      pf_store_forward_file_path^ [4] := nfc$sf_permanent_file_name;
      pf_access_mode := $pft$usage_selections [pfc$read];
      pf_share_mode := $pft$usage_selections [pfc$read];
      pf_password := osc$null_name;
      pf_cycle_selection.cycle_option := pfc$highest_cycle;
      pmp$get_unique_name (store_forward_file_info.local_file_name, status);
      attach_count := 0;

{ pfp$attach is used to attach the System's Store/Forward Network file because
{ we want to ensure that each instance of fsp$open_file opens the same cycle of this file
{ which is accomplished by using the local file name.  The local file name is also needed
{ to allow different tasks within the same job to open the same file.  A file identifier
{ cannot be passed between the various tasks within the job.

      REPEAT
        pfp$attach (store_forward_file_info.local_file_name, pf_store_forward_file_path^, pf_cycle_selection,
              pf_password, pf_access_mode, pf_share_mode, pfc$no_wait, status);
        IF NOT status.normal THEN

{ If the store_forward_network file is busy wait 15 seconds and then retry again

          IF NOT (status.condition = pfe$cycle_busy) THEN
            store_forward_file_info.local_file_name := osc$null_name;
            RETURN;
          IFEND;
          attach_count := attach_count + 1;
          pmp$wait (delay_time_for_attach, 0);
        IFEND;
      UNTIL (status.normal) OR (attach_count >= max_attach_tries);

{ if the store_forward_network file is still busy after max_attach_tries return with the bad status

      IF (NOT status.normal) AND (attach_count >= max_attach_tries) THEN
        RETURN;
      IFEND;
    IFEND;

{ open the store_forward_network file with access and share modes of READ only.

    PUSH ptr_file_attachment_options: [1 .. 2];
    ptr_file_attachment_options^ [1].selector := fsc$access_and_share_modes;
    ptr_file_attachment_options^ [1].access_modes.selector := fsc$specific_access_modes;
    ptr_file_attachment_options^ [1].access_modes.value := $fst$file_access_options [fsc$read];
    ptr_file_attachment_options^ [1].share_modes.selector := fsc$specific_share_modes;
    ptr_file_attachment_options^ [1].share_modes.value := $fst$file_access_options [fsc$read];
    ptr_file_attachment_options^ [2].selector := fsc$open_position;
    ptr_file_attachment_options^ [2].open_position := amc$open_at_boi;

    fsp$open_file (store_forward_file_info.local_file_name, amc$segment, ptr_file_attachment_options, NIL,
          NIL, NIL, NIL, store_forward_file_info.file_identifier, status);

    store_forward_file_info.file_open := status.normal;
    IF NOT status.normal THEN
      IF attach_file THEN
        amp$return (store_forward_file_info.local_file_name, ignore_status);
        store_forward_file_info.local_file_name := osc$null_name;
      IFEND;

{ if the user did not request to attach the store_forward_network file and
{ this file was not previously attached the error return by fsp$open_file
{ will be ame$new_file_requires_append.

      IF status.condition = ame$new_file_requires_append THEN
        osp$set_status_abnormal (nfc$status_id, nfe$sf_no_store_forward_network, ' ', status);
      IFEND;
      RETURN;
    IFEND;

    pmp$establish_condition_handler (exit_condition, ^attach_sf_file_cond_handler, ^establish_descriptor,
          status);
    IF NOT status.normal THEN
      fsp$close_file (store_forward_file_info.file_identifier, ignore_status);
      IF attach_file THEN
        amp$return (store_forward_file_info.local_file_name, ignore_status);
        store_forward_file_info.local_file_name := osc$null_name;
      IFEND;
      store_forward_file_info.file_open := FALSE;
      RETURN;
    IFEND;

{ get the relative pointers for the various lists from the first part of the file

    amp$get_segment_pointer (store_forward_file_info.file_identifier, amc$sequence_pointer,
          store_forward_file_info.segment_pointer, status);
    RESET store_forward_file_info.segment_pointer.sequence_pointer;
    NEXT ptr_store_forward_file_ptrs IN store_forward_file_info.segment_pointer.sequence_pointer;

{ if the initial pointer for the file is NIL then the file is bad

    IF ptr_store_forward_file_ptrs = NIL THEN
      read_error := TRUE;
    IFEND;

    pmp$disestablish_cond_handler (exit_condition, ignore_status);

    IF read_error THEN

{ if an error has occurred close the store_forward_file

      fsp$close_file (store_forward_file_info.file_identifier, ignore_status);
      IF attach_file THEN
        amp$return (store_forward_file_info.local_file_name, ignore_status);
        store_forward_file_info.local_file_name := osc$null_name;
      IFEND;
      store_forward_file_info.file_open := FALSE;
      store_forward_file_info.segment_pointer.sequence_pointer := NIL;
      osp$set_status_abnormal (nfc$status_id, nfe$sf_read_network_file_error, ' ', status);
    ELSE
      store_forward_file_info.pointers := ptr_store_forward_file_ptrs^;
    IFEND;
  PROCEND nfp$open_store_forward_file;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nfp$close_store_forward_file', EJECT ??

*copyc nfh$close_store_forward_file

  PROCEDURE [XDCL] nfp$close_store_forward_file
    (VAR store_forward_file_info: nft$store_forward_file_info;
     VAR status: ost$status);

    VAR
      ignore_status: ost$status;

    status.normal := TRUE;

{ set all pointers to the various lists to NIL and file_open to FALSE

    store_forward_file_info.file_open := FALSE;
    store_forward_file_info.segment_pointer.sequence_pointer := NIL;
    store_forward_file_info.pointers.ptr_application_name_list := NIL;
    store_forward_file_info.pointers.ptr_group_name_list := NIL;
    store_forward_file_info.pointers.ptr_source_name_list := NIL;
    store_forward_file_info.pointers.ptr_target_name_list := NIL;

    fsp$close_file (store_forward_file_info.file_identifier, status);

    amp$return (store_forward_file_info.local_file_name, ignore_status);
    store_forward_file_info.local_file_name := osc$null_name;
  PROCEND nfp$close_store_forward_file;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nfp$get_new_application_name', EJECT ??

*copyc nfh$get_new_application_name

  PROCEDURE [XDCL] nfp$get_new_application_name
    (    application_qualifier: nft$sf_applications;
         store_forward_file_info: nft$store_forward_file_info;
         destination_name: nft$parameter_24_definition;
     VAR application_name_changed: boolean;
     VAR new_application_name: ost$name;
     VAR status: ost$status);

    VAR
      destination_names_index: ost$non_negative_integers,
      ptr_current_application_info: ^nft$sf_application_name_info,
      ptr_current_dest_names_info: ^nft$sf_destination_names_array,
      ptr_current_group_name_info: ^nft$sf_group_name_information;

    status.normal := TRUE;
    application_name_changed := FALSE;

{ pointer to the first entry in the application list

    ptr_current_application_info := #PTR (store_forward_file_info.pointers.ptr_application_name_list,
          store_forward_file_info.segment_pointer.sequence_pointer^);

  /find_new_application_name/
    WHILE ptr_current_application_info <> NIL DO
      IF NOT ptr_current_application_info^.link.relative_pointer THEN
        osp$set_status_abnormal (nfc$status_id, nfe$sf_internal_error_bad_rptr,
              'NFP$GET_NEW_APPLICATION_NAME 1', status);
        RETURN;
      IFEND;

{ check to see if the requesting application is valid for this application entry

      IF application_qualifier IN ptr_current_application_info^.application_qualifier THEN
        ptr_current_group_name_info := #PTR (store_forward_file_info.pointers.ptr_group_name_list,
              store_forward_file_info.segment_pointer.sequence_pointer^);

{ find the destination group entry from the current application entry in the group name list

        WHILE ptr_current_group_name_info <> NIL DO
          IF NOT ptr_current_group_name_info^.link.relative_pointer THEN
            osp$set_status_abnormal (nfc$status_id, nfe$sf_internal_error_bad_rptr,
                  'NFP$GET_NEW_APPLICATION_NAME 2', status);
            RETURN;
          IFEND;
          IF NOT ptr_current_group_name_info^.ptr_destination_names.relative_pointer THEN
            osp$set_status_abnormal (nfc$status_id, nfe$sf_internal_error_bad_rptr,
                  'NFP$GET_NEW_APPLICATION_NAME 3', status);
            RETURN;
          IFEND;

{ check to see if this is the destination group qualifier from the current application entry

          IF ptr_current_application_info^.destination_group_qualifier =
                ptr_current_group_name_info^.group_name THEN

            ptr_current_dest_names_info := #PTR (ptr_current_group_name_info^.ptr_destination_names.
                  relative_ptr, store_forward_file_info.segment_pointer.sequence_pointer^);
            FOR destination_names_index := 1 TO ptr_current_group_name_info^.destination_name_count DO

{ check to see if the destination name is in the destination
{ group qualifier for this application entry

              IF destination_name = ptr_current_dest_names_info^ [destination_names_index] THEN
                new_application_name := ptr_current_application_info^.next_hop_application;
                application_name_changed := TRUE;
                EXIT /find_new_application_name/;
              IFEND;
            FOREND;
          IFEND;
          ptr_current_group_name_info := #PTR (ptr_current_group_name_info^.link.relative_ptr,
                store_forward_file_info.segment_pointer.sequence_pointer^);
        WHILEND;
      IFEND;
      ptr_current_application_info := #PTR (ptr_current_application_info^.link.relative_ptr,
            store_forward_file_info.segment_pointer.sequence_pointer^);
    WHILEND /find_new_application_name/;
  PROCEND nfp$get_new_application_name;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nfp$get_new_destination_name', EJECT ??

*copyc nfh$get_new_destination_name

  PROCEDURE [XDCL] nfp$get_new_destination_name
    (    application_qualifier: nft$sf_applications;
         store_forward_file_info: nft$store_forward_file_info;
         current_target_name: nft$parameter_24_definition;
     VAR target_name_changed: boolean;
     VAR new_target_name: nft$parameter_24_definition;
     VAR status: ost$status);

    VAR
      ptr_current_group_name_info: ^nft$sf_group_name_information,
      ptr_current_target_name_info: ^nft$sf_target_name_information;

    status.normal := TRUE;
    target_name_changed := FALSE;

{ pointer to the first entry in the target name list

    ptr_current_target_name_info := #PTR (store_forward_file_info.pointers.ptr_target_name_list,
          store_forward_file_info.segment_pointer.sequence_pointer^);

  /find_new_target_name/
    WHILE ptr_current_target_name_info <> NIL DO
      IF NOT ptr_current_target_name_info^.link.relative_pointer THEN
        osp$set_status_abnormal (nfc$status_id, nfe$sf_internal_error_bad_rptr,
              'NFP$GET_NEW_DESTINATION_NAME', status);
        RETURN;
      IFEND;

{ check to see if the requesting application is valid for this target name entry

      IF (application_qualifier IN ptr_current_target_name_info^.application_qualifier) AND
            (current_target_name = ptr_current_target_name_info^.target_name) THEN

        new_target_name := ptr_current_target_name_info^.next_hop_name;
        target_name_changed := TRUE;
        EXIT /find_new_target_name/;
      IFEND;
      ptr_current_target_name_info := #PTR (ptr_current_target_name_info^.link.relative_ptr,
            store_forward_file_info.segment_pointer.sequence_pointer^);
    WHILEND /find_new_target_name/;
  PROCEND nfp$get_new_destination_name;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nfp$get_new_source_name', EJECT ??

*copyc nfh$get_new_source_name

  PROCEDURE [XDCL] nfp$get_new_source_name
    (    application_qualifier: nft$sf_applications;
         store_forward_file_info: nft$store_forward_file_info;
         current_source_name: nft$parameter_24_definition;
         destination_name: nft$parameter_24_definition;
     VAR source_name_changed: boolean;
     VAR new_source_name: nft$parameter_24_definition;
     VAR status: ost$status);

    VAR
      destination_names_index: ost$non_negative_integers,
      ptr_current_dest_names_info: ^nft$sf_destination_names_array,
      ptr_current_group_name_info: ^nft$sf_group_name_information,
      ptr_current_source_name_info: ^nft$sf_source_name_information;

    status.normal := TRUE;
    source_name_changed := FALSE;

{ pointer to the first entry in the source name list

    ptr_current_source_name_info := #PTR (store_forward_file_info.pointers.ptr_source_name_list,
          store_forward_file_info.segment_pointer.sequence_pointer^);

  /find_new_source_name/
    WHILE ptr_current_source_name_info <> NIL DO
      IF NOT ptr_current_source_name_info^.link.relative_pointer THEN
        osp$set_status_abnormal (nfc$status_id, nfe$sf_internal_error_bad_rptr, 'NFP$GET_NEW_SOURCE_NAME 1',
              status);
        RETURN;
      IFEND;

{ check to see if the requesting application is valid for this source name entry

      IF (application_qualifier IN ptr_current_source_name_info^.application_qualifier) AND
            (current_source_name = ptr_current_source_name_info^.source_name) THEN
        ptr_current_group_name_info := #PTR (store_forward_file_info.pointers.ptr_group_name_list,
              store_forward_file_info.segment_pointer.sequence_pointer^);

{ find the destination group entry from the current source name entry in the group name list

        WHILE ptr_current_group_name_info <> NIL DO
          IF NOT ptr_current_group_name_info^.link.relative_pointer THEN
            osp$set_status_abnormal (nfc$status_id, nfe$sf_internal_error_bad_rptr,
                  'NFP$GET_NEW_SOURCE_NAME 2', status);
            RETURN;
          IFEND;
          IF NOT ptr_current_group_name_info^.ptr_destination_names.relative_pointer THEN
            osp$set_status_abnormal (nfc$status_id, nfe$sf_internal_error_bad_rptr,
                  'NFP$GET_NEW_SOURCE_NAME 3', status);
            RETURN;
          IFEND;

{ check to see if this is the destination group qualifier from the current source name entry

          IF ptr_current_source_name_info^.destination_group_qualifier =
                ptr_current_group_name_info^.group_name THEN

            ptr_current_dest_names_info := #PTR (ptr_current_group_name_info^.ptr_destination_names.
                  relative_ptr, store_forward_file_info.segment_pointer.sequence_pointer^);
            FOR destination_names_index := 1 TO ptr_current_group_name_info^.destination_name_count DO

{ check to see if the destination name is in the destination
{ group qualifier for this source name entry

              IF destination_name = ptr_current_dest_names_info^ [destination_names_index] THEN
                new_source_name := ptr_current_source_name_info^.next_hop_name;
                source_name_changed := TRUE;
                EXIT /find_new_source_name/;
              IFEND;
            FOREND;
          IFEND;
          ptr_current_group_name_info := #PTR (ptr_current_group_name_info^.link.relative_ptr,
                store_forward_file_info.segment_pointer.sequence_pointer^);
        WHILEND;
      IFEND;
      ptr_current_source_name_info := #PTR (ptr_current_source_name_info^.link.relative_ptr,
            store_forward_file_info.segment_pointer.sequence_pointer^);
    WHILEND /find_new_source_name/;
  PROCEND nfp$get_new_source_name;
?? OLDTITLE ??
MODEND nfm$store_forward_utilities;
