?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE : Basic Access Methods : Open File' ??
MODULE fsm$open_file;
?? RIGHT := 110 ??
?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc amt$access_level
*copyc amt$fap_declarations
*copyc amt$file_identifier
*copyc fme$file_management_errors
*copyc fsc$local
*copyc fse$open_validation_errors
*copyc fst$file_reference
*copyc jmt$job_mode
*copyc osc$data_retrieval_req_cond
*copyc osd$exception_policies
*copyc oss$job_paged_literal
*copyc ost$caller_identifier
*copyc pfe$external_archive_conditions
?? POP ??
*copyc pmf$job_mode
*copyc bap$close
*copyc bap$end_new_open_processing
*copyc bap$free_static_label
*copyc bap$mark_fap_layer_open
*copyc bap$open_file
*copyc bap$return
*copyc clp$convert_cyc_ref_to_cyc_sel
*copyc clp$convert_file_ref_to_string
*copyc clp$evaluate_file_reference
*copyc clp$trimmed_string_size
*copyc fsp$close_file
*copyc fsp$convert_fs_structure_to_pf
*copyc fsp$path_element
*copyc fsp$resolve_file_reference
*copyc fsp$set_evaluated_file_abnormal
*copyc osp$await_activity_completion
*copyc osp$disestablish_cond_handler
*copyc osp$enforce_exception_policies
*copyc osp$establish_block_exit_hndlr
*copyc osp$establish_condition_handler
*copyc osp$file_access_condition
*copyc osp$generate_log_message
*copyc pfp$purge
*copyc pfp$retrieve_archived_file
*copyc pmp$cause_condition

*copyc bav$task_file_table
*copyc osv$initial_exception_context
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

  CONST
    max_volumes_per_file = 500;

  VAR
    amv$nil_file_identifier: [XDCL, #GATE, READ, oss$job_paged_literal] amt$file_identifier := [0, 1];

?? OLDTITLE ??
*copyc amh$also
*copyc fsh$open_file
?? NEWTITLE := 'PROCEDURE [XDCL, #GATE] fsp$open_file', EJECT ??

  PROCEDURE [XDCL, #GATE] fsp$open_file
    (    file: fst$file_reference;
         access_level: amt$access_level;
         file_attachment: ^fst$attachment_options;
         default_creation_attributes: ^fst$file_cycle_attributes;
         mandated_creation_attributes: ^fst$file_cycle_attributes;
         attribute_validation: ^fst$file_cycle_attributes;
         attribute_override: ^fst$file_cycle_attributes;
     VAR file_identifier: amt$file_identifier;
     VAR status: ost$status);

    CONST
      fap_layer_number = 0;

    VAR
      archive_cycle_number: fst$cycle_number,
      call_block: amt$call_block,
      contains_data: boolean,
      context: ^ost$ecp_exception_context,
      evaluated_file_reference: fst$evaluated_file_reference,
      local_file_identifier: amt$file_identifier,
      local_status: ost$status,
      raised_conditions: fst$file_access_conditions,
      task_file_entry_p: ^bat$task_file_entry,
      time_since_last_retrieval: 0 .. fsc$longest_wait_time;

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

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

      IF (local_file_identifier <> amv$nil_file_identifier) THEN
        clean_up;
      IFEND;

    PROCEND block_exit_handler;
?? OLDTITLE ??
?? NEWTITLE := '  PROCEDURE clean_up ', EJECT ??

    PROCEDURE clean_up;

      VAR
        clean_up_status: ost$status,
        context: ost$ecp_exception_context,
        cycle_selector: clt$cycle_selector,
        initial_open: boolean,
        open_created_temporary_file: boolean,
        pf_path: ^pft$path;

{ Call to pfp$purge should occur before call to fsp$close_file so that
{ the file is deleted before access is allowed to other users.

      IF (fsp$path_element (^evaluated_file_reference, 1) ^ <> fsc$local) AND
            bav$task_file_table^ [local_file_identifier.ordinal].open_actions.open_created_file THEN
        PUSH pf_path: [1 .. evaluated_file_reference.number_of_path_elements];
        fsp$convert_fs_structure_to_pf (evaluated_file_reference, pf_path);
        clp$convert_cyc_ref_to_cyc_sel (evaluated_file_reference.cycle_reference, cycle_selector);
        context := osv$initial_exception_context;
        get_attachment_options (file_attachment, context);
        pfp$purge (pf_path^, cycle_selector.value, context.password, clean_up_status);
      IFEND;

      open_created_temporary_file := (fsp$path_element (^evaluated_file_reference,
            1) ^ = fsc$local) AND bav$task_file_table^ [local_file_identifier.ordinal].open_actions.
            open_created_file;

      IF bav$task_file_table^ [local_file_identifier.ordinal].initial_open THEN
        { discard label
        bap$free_static_label (evaluated_file_reference.path_handle_info.path_handle);
{ We should retrieve the static label for a permanent file that isn't
{ being returned.  The following example currently fails:
{ cref $user.x; setfa $user.x fc=george; detf $user.x; attf $user.x
{ open $user.x - failing open
{ open $user.x - successful open but setfa is lost.
        initial_open := TRUE;
      IFEND;

      fsp$close_file (local_file_identifier, clean_up_status);

      IF initial_open THEN
{ Force close to occur on a creation open, this is to prevent
{ the situation of file_previously_open=FALSE & open_count=1; this
{ would cause open to loop forever, because open would believe that
{ an asynchronous open was in progress.
        bap$close (local_file_identifier, clean_up_status);
      IFEND;
      IF open_created_temporary_file THEN
        bap$return (evaluated_file_reference, {detachment_options} NIL, clean_up_status);
      IFEND;

    PROCEND clean_up;
?? OLDTITLE ??
?? EJECT ??

    status.normal := TRUE;
    local_status.normal := TRUE;
    context := NIL;
    file_identifier := amv$nil_file_identifier;
    local_file_identifier := amv$nil_file_identifier;
    #SPOIL (local_file_identifier);
    raised_conditions := $fst$file_access_conditions [];
    time_since_last_retrieval := 0;

    clp$evaluate_file_reference (file, $clt$file_ref_parsing_options [clc$use_$local_as_working_cat],
          {resolve_cycle_number=} FALSE, evaluated_file_reference, local_status);
    IF local_status.normal THEN
      osp$establish_block_exit_hndlr (^block_exit_handler);

      REPEAT
        {evaluated_file_reference is only updated when the open is successful.
        bap$open_file (access_level, file_attachment, default_creation_attributes,
              mandated_creation_attributes, attribute_validation, attribute_override,
              evaluated_file_reference, contains_data, local_file_identifier, archive_cycle_number,
              local_status);

        IF local_status.normal THEN
          IF (context <> NIL) THEN
            context^ := osv$initial_exception_context;
          IFEND;
        ELSE
          IF context = NIL THEN
            PUSH context;
            context^ := osv$initial_exception_context;
            context^.caller_will_retrieve_file := TRUE;
          IFEND;
          process_abnormal_status (archive_cycle_number, file_attachment, evaluated_file_reference,
                local_status, time_since_last_retrieval, context^);
        IFEND;
      UNTIL local_status.normal OR ((context <> NIL) AND (NOT context^.wait));

      IF local_status.normal THEN
        bap$mark_fap_layer_open (local_file_identifier, fap_layer_number, local_status);
        IF local_status.normal THEN
          task_file_entry_p := ^bav$task_file_table^ [local_file_identifier.ordinal];
          call_block.operation := amc$open_req;
          call_block.open.access_level := access_level;
          call_block.open.local_file_name := task_file_entry_p^.local_file_name;
          call_block.open.contains_data := contains_data;
          call_block.open.existing_file := NOT task_file_entry_p^.initial_open;

          task_file_entry_p^.fap_control_information.first_fap.
                access_method^ (local_file_identifier, call_block, fap_layer_number, local_status);
          IF local_status.normal AND task_file_entry_p^.initial_open THEN
            REPEAT
              bap$end_new_open_processing (evaluated_file_reference.path_handle_info.path_handle,
                    local_status);
              IF NOT local_status.normal THEN
                IF context = NIL THEN
                  PUSH context;
                  context^ := osv$initial_exception_context;
                  context^.raised_conditions := $fst$file_access_conditions [];
                IFEND;
                process_abnormal_status (archive_cycle_number, file_attachment, evaluated_file_reference,
                      local_status, time_since_last_retrieval, context^);
              IFEND;
            UNTIL local_status.normal OR ((context <> NIL) AND (NOT context^.wait));
          IFEND;
        IFEND;
      IFEND;
    IFEND;

    IF local_status.normal THEN
      file_identifier := local_file_identifier;
    ELSE
      status := local_status;
      IF (local_file_identifier <> amv$nil_file_identifier) THEN
        clean_up;
      IFEND;
    IFEND;

    osp$disestablish_cond_handler;

  PROCEND fsp$open_file;

?? NEWTITLE := 'GET_ATTACHMENT_OPTIONS', EJECT ??

  PROCEDURE [INLINE] get_attachment_options
    (    file_attachment: ^fst$attachment_options;
     VAR context: ost$ecp_exception_context);

{ DESIGN: Prior to R1.5.3, the default policy was to wait for all conditions
{         except cycle busy.  However, the new policy is to have the default
{         influenced by job mode.  If the caller is a batch job, we now wait
{         indefinitely for all conditions.  If the caller is an interactive job,
{         we invoke the default condition handler to let the interactive user
{         decide the wait policy.

    VAR
      allowed_conditions_defaulted: boolean,
      index: integer,
      wait_defaulted: boolean;

    allowed_conditions_defaulted := TRUE;
    wait_defaulted := TRUE;

    context.password := osc$null_name;
    context.allowed_access_conditions := -$fst$file_access_conditions [fsc$cycle_busy];
    context.wait := TRUE;
    context.wait_time := fsc$longest_wait_time;

    IF pmf$job_mode () = jmc$batch THEN
      context.allowed_access_conditions := -$fst$file_access_conditions [];
    IFEND;

    IF file_attachment <> NIL THEN
      FOR index := LOWERBOUND (file_attachment^) TO UPPERBOUND (file_attachment^) DO
        CASE file_attachment^ [index].selector OF
        = fsc$allowed_exceptions =
          context.allowed_access_conditions := file_attachment^ [index].allowed_exceptions.access_conditions;
          allowed_conditions_defaulted := FALSE;
        = fsc$password =
          context.password := file_attachment^ [index].password;
        = fsc$wait_for_attachment =
          context.wait := file_attachment^ [index].wait_for_attachment.wait = osc$wait;
          IF context.wait THEN
            context.wait_time := file_attachment^ [index].wait_for_attachment.wait_time;
          IFEND;
          wait_defaulted := FALSE;
        ELSE
        CASEND;
      FOREND;
      IF allowed_conditions_defaulted AND (NOT wait_defaulted) AND context.wait THEN
        context.allowed_access_conditions := -$fst$file_access_conditions [];
      IFEND;
    IFEND;

  PROCEND get_attachment_options;

?? OLDTITLE ??
?? NEWTITLE := 'PROCESS_ABNORMAL_STATUS', EJECT ??

  PROCEDURE process_abnormal_status
    (    archive_cycle_number: fst$cycle_number;
         file_attachment: ^fst$attachment_options;
         evaluated_file_reference: fst$evaluated_file_reference;
     VAR status {input, output} : ost$status;
     VAR time_since_last_retrieval {input, output} : 0 .. fsc$longest_wait_time;
     VAR context {input, output} : ost$ecp_exception_context);

{ DESIGN: This procedure may be called repetitively and may process different
{         abnormal conditions.  CONTEXT.RAISED_CONDITIONS is a control variable that
{         determines whether the condition is new or not.  If new, we raise the
{         condition.  If we wait for the condition, this procedure is reentered
{         at the polling frequency until either the condition clears or the
{         CONTEXT.WAIT_TIME is exhausted.
{
{         It is possible (but conflicting) for a program to specify a non null
{         set for ALLOWED_EXCEPTIONS.FILE_ACCESS_CONDITIONS and to specify
{         no wait for WAIT_FOR_ATTACHMENT; this is implemented as a no wait.

    CONST
      five_minutes = 300000;

    VAR
      local_status: ost$status,
      pf_path: ^pft$path,
      ready_index: integer,
      wait_list: array [1 .. 1] of ost$activity;

?? EJECT ??

    IF osp$file_access_condition (status) THEN

      context.condition_status := status;

      IF context.raised_conditions = $fst$file_access_conditions [] THEN
        get_attachment_options (file_attachment, context);
        context.file.selector := osc$ecp_evaluated_file_ref;
        context.file.evaluated_file_reference := evaluated_file_reference;
      IFEND;

      IF context.wait THEN
        IF status.condition = pfe$cycle_data_resides_offline THEN
          IF NOT (fsc$data_retrieval_required IN context.raised_conditions) THEN
            pmp$cause_condition (osc$data_retrieval_req_cond, ^context {input, output} , local_status);
            IF local_status.normal THEN
              context.raised_conditions := context.raised_conditions +
                    $fst$file_access_conditions [fsc$data_retrieval_required]
            IFEND;
          IFEND;
          IF ((time_since_last_retrieval = 0) OR (time_since_last_retrieval >= five_minutes)) THEN
            PUSH pf_path: [1 .. evaluated_file_reference.number_of_path_elements];
            fsp$convert_fs_structure_to_pf (evaluated_file_reference, pf_path);
            pfp$retrieve_archived_file (pf_path^, archive_cycle_number, context.password, osc$nowait,
                  local_status);

            context.wait := local_status.normal;

            IF NOT local_status.normal THEN
              status := local_status;
              RETURN; {----->
            IFEND;

            time_since_last_retrieval := 0;
          IFEND;
        IFEND;

        osp$enforce_exception_policies (context);
        status := context.condition_status;
        time_since_last_retrieval := time_since_last_retrieval + context.elapsed_wait_time;
      IFEND;
    ELSE {Not a long term wait condition controlled by file_access_conditions}
      CASE status.condition OF
      = fme$reattach_detached_file, fme$wait_for_open_lock, pfe$tape_attached_on_client =
        { The preceding conditions are transient conditions requiring a short wait}
        wait_list [1].activity := osc$await_time;
        wait_list [1].milliseconds := 5000;
        osp$await_activity_completion (wait_list, ready_index, local_status);
        context.wait := TRUE;

      ELSE
        context.wait := FALSE;
      CASEND;
    IFEND;
  PROCEND process_abnormal_status;
?? OLDTITLE ??
MODEND fsm$open_file;


