?? RIGHT := 110 ??
?? NEWTITLE := 'INSTALL_SOFTWARE Utility: RAP$CORRECT_PRODUCTS Interface.' ??
MODULE ram$correct_products;

{ PURPOSE:
{   This module contains the interface and procedures that correct
{   a subproduct or subproducts during installation.
{
{ DESIGN:
{   Refer to design for validate for correction for a description of the
{   correction process.
{
{   The compiled module resides in RAF$LIBRARY.
{ NOTES:
{

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc rac$installation_cycles
*copyc rae$install_software_cc
*copyc fst$number_of_path_elements
*copyc ost$caller_identifier
*copyc rat$element_paths
*copyc rat$installation_control_record
?? POP ??
*copyc amp$get_file_attributes
*copyc amp$return
*copyc avp$ring_min
*copyc clp$convert_integer_to_string
*copyc clp$include_line
*copyc fsp$copy_file
*copyc ocp$apply_object_correction
*copyc osp$append_status_file
*copyc osp$disestablish_cond_handler
*copyc osp$establish_block_exit_hndlr
*copyc osp$generate_log_message
*copyc osp$set_status_abnormal
*copyc pfp$change
*copyc pfp$define_catalog
*copyc pfp$find_directory_array
*copyc pfp$find_direct_info_record
*copyc pfp$find_next_info_record
*copyc pfp$find_permit_array
*copyc pfp$get_item_info
*copyc pfp$permit
*copyc pfp$permit_catalog
*copyc pfp$purge
*copyc pmp$get_unique_name
*copyc rap$checksum_file
*copyc rap$clear_installation
*copyc rap$convert_path_to_str
*copyc rap$get_cycle_data
*copyc rap$get_majority_file_class
*copyc rap$record_step_status
*copyc rap$record_subproduct_status
*copyc rmp$request_mass_storage
?? OLDTITLE, NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??
?? OLDTITLE, NEWTITLE := '[XDCL] rap$correct_products', EJECT ??

{ PURPOSE:
{   This interface controls the step to apply subproduct corrections.
{
{ DESIGN:
{   The correct products step corrects all subproducts required for the
{   installation.  This occurs between the loading and staging steps.
{
{   This step follows standard step design.
{
{   The subproduct correction process is driven by the element list for
{   the correction.  The element list is processed by the standard
{   element list traversal algorithm.  The processing that occurs for
{   each element is based on the correction_directives, whether the
{   element is active or inactive and the installation_scheme for the
{   subproduct.
{
{   The corrections or replacement files were placed in the loading cycle
{   prior to executing this step.  When a correction has format of object
{   library, then the correction base is required to apply the
{   correction.  The base is located using the correction directives.
{   The corrected file is written to the staging cycle.  Since the
{   staging process expects files in the loading cycle only, the
{   correction is deleted and the corrected file is copied back to the
{   loading cycle.  When files are corrected by file replacement, no
{   correction base file is needed.
{
{   To improve the performance of the correction process, files are not
{   corrected if a previously corrected version of the file is available
{   on the system.  (This can be overridden by the user).  When a
{   previous correction is used, the processing needed is based on each
{   subproduct's correction installation scheme.  For cycle corrections,
{   it is only necessary to delete the correction file from the loading
{   cycle.  For version based, the correction file must be deleted and
{   the previously corrected file copied into the loading cycle so it can
{   be staged and activated later.
{
{   One twist of version based corrections, is that any file/catalog
{   which is not corrected for a subproduct, must have the released
{   version of the file brought forward into the new version catalog.
{
{   The inability to correct any file belonging to a subproduct will cause
{   the entire subproduct to fail the correction step.  The installation
{   processing record for that subproduct is set as such and the subproduct
{   will be cleared from the system at the conclusion of this step.
{
{   The failure of one subproduct does not jeopardize the remaining
{   subproducts.  Each subproduct is processed independently.
{
{   For certain portions of the correction process, it may be necessary
{   process files which are not readable at this job's current execution
{   ring.  To solve this, the file is copied to a $LOCAL.$UNIQUE file
{   for processing.  To ensure this file is returned in an abort situtation,
{   the file name is assigned once for the entire step and the file is
{   is returned by this step's abort handler.
{
{ NOTES:
{   The SUBPRODUCT_FAILED_PROCESSING boolean has been initialized outside of
{   this interface and should never be initialized here.
{

  PROCEDURE [XDCL] rap$correct_products
    (VAR installation_control_record {input, output} : rat$installation_control_record;
     VAR subproducts_failed_processing: boolean;
     VAR status: ost$status);

    VAR
      subproduct_index: rat$subproduct_count,
      ignore_keyword: ost$name,
      ignore_status: ost$status,
      local_status: ost$status,
      majority_file_class: rmt$mass_storage_class,
      processing_record: rat$subp_processing_record,
      scratch_file: rat$path,
      subproduct_paths: rat$element_paths,
      task_status: ost$status,
      unique_name: ost$name;

?? NEWTITLE := 'abort_handler', EJECT ??

{ PURPOSE:
{   This procedure cleans up when an abort situation occurs within the
{   block structure.
{
{ DESIGN:
{   The function of this condition handler is to return the temporary file
{   used by this interface in an abort situation.

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

      VAR
        ignore_status: ost$status;

      IF scratch_file.size <> 0 THEN
        amp$return (scratch_file.path (1, scratch_file.size), ignore_status);
      IFEND;

    PROCEND abort_handler;
?? OLDTITLE, EJECT ??

    status.normal := TRUE;

    IF NOT (rac$correct_subproducts_step IN installation_control_record.processing_header_p^.step_set) THEN
      RETURN;
    IFEND;

    rap$record_step_status (rac$correct_subproducts_step, rac$step_started, installation_control_record,
          status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    pmp$get_unique_name (unique_name, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    STRINGREP (scratch_file.path, scratch_file.size, ':$LOCAL.', unique_name);

    osp$establish_block_exit_hndlr (^abort_handler);

  /main/
    BEGIN

      FOR subproduct_index := 1 TO UPPERBOUND (installation_control_record.subproduct_processing_records_p^)
            DO
        processing_record := installation_control_record.subproduct_processing_records_p^
              [subproduct_index];

        IF (installation_control_record.job_identifier = processing_record.job_identifier) AND
              (rac$correct_files_task IN processing_record.task_set) AND
              (processing_record.task_status <> rac$task_failed) THEN

          rap$record_subproduct_status (rac$correct_files_task, rac$task_started, subproduct_index,
                installation_control_record, ignore_status);

          rap$get_majority_file_class (subproduct_index, installation_control_record, ignore_keyword,
                majority_file_class, status);
          IF NOT status.normal THEN
            EXIT /main/;
          IFEND;

          task_status.normal := TRUE;

          establish_subproduct_paths (processing_record, installation_control_record.processing_seq_p,
                subproduct_paths);

          correct_subproduct (processing_record.subproduct_info_pointers.element_list_p, subproduct_paths,
                scratch_file, processing_record.subproduct_info_pointers.attributes_p^.installation_scheme,
                majority_file_class, installation_control_record.processing_header_p^.installation_defaults.
                ignore_storage_class, processing_record.subproduct_info_pointers.subproduct_info_seq_p,
                task_status);

          IF task_status.normal THEN
            rap$record_subproduct_status (rac$correct_files_task, rac$task_completed, subproduct_index,
                  installation_control_record, ignore_status);
          ELSE
            osp$generate_log_message ($pmt$ascii_logset [pmc$job_log], task_status, ignore_status);
            rap$record_subproduct_status (rac$correct_files_task, rac$task_failed, subproduct_index,
                  installation_control_record, ignore_status);
            subproducts_failed_processing := TRUE;
          IFEND;

        IFEND;
      FOREND;

    END /main/;

    rap$clear_installation (installation_control_record, ignore_status);

    amp$return (scratch_file.path (1, scratch_file.size), ignore_status);

    osp$disestablish_cond_handler;

    rap$record_step_status (rac$correct_subproducts_step, rac$step_completed, installation_control_record,
          local_status);
    IF status.normal AND (NOT local_status.normal) THEN
      status := local_status;
    IFEND;

  PROCEND rap$correct_products;

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

{ PURPOSE:
{   This procedure applies a correction to an object library
{   and places the corrected file in the loading cycle on a device of the
{   majority storage class.
{
{ DESIGN:
{   The first requirement is to prepare the correction base file.  This
{   involves getting the path to where the file resides and possibly updating
{   the correction bases catalog.
{
{   Since the loading cycle is already occupied by the files' correction, the
{   corrected file must be written temporarily to the staging cycle.  The
{   loading cycle is then deleted, and the corrected file is put in the loading
{   cycle in the majority storage class that was used for the subproduct load.
{
{ NOTES:
{   When moving the corrected file from the staging cycle to the loading cycle,
{   it must be copied since we do not know what storage class the correction
{   was created on.

  PROCEDURE apply_object_correction
    (    element_p: ^rat$element;
         element_paths: rat$element_paths;
         scratch_file: rat$path;
         majority_file_class: rmt$mass_storage_class;
         installation_scheme: rat$installation_scheme;
         ignore_storage_class: boolean;
     VAR status: ost$status);


    VAR
      base_file: rat$path,
      corrected_file: rat$path,
      correction_base_catalog_updated: boolean,
      cycle_selector: pft$cycle_selector,
      ignore_status: ost$status,
      password: pft$password,
      patch_file: rat$path,
      staging_cycle: array [1 .. 1] of pft$change_descriptor;


    status.normal := TRUE;

    password := ' ';
    cycle_selector.cycle_option := pfc$specific_cycle;
    cycle_selector.cycle_number := rac$loading_cycle;

    prepare_correction_base_file (element_p, installation_scheme,
          element_paths [rac$correction_base_cat_path]^, element_paths [rac$base_level_path]^, scratch_file,
          base_file, correction_base_catalog_updated, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    rap$convert_path_to_str (element_paths [rac$installation_catalog_path]^, patch_file);
    STRINGREP (corrected_file.path, corrected_file.size, patch_file.path (1, patch_file.size), '.',
          rac$staging_cycle_str);
    STRINGREP (patch_file.path, patch_file.size, patch_file.path (1, patch_file.size),
          '.', rac$loading_cycle_str);

    ocp$apply_object_correction (base_file.path (1, base_file.size),
          patch_file.path (1, patch_file.size), corrected_file.path (1, corrected_file.size),
          status);
    IF NOT status.normal THEN
      IF (status.condition = rae$corr_base_checksum_mismatch) THEN
        IF (correction_base_catalog_updated) THEN
          { Get rid of bad file in the correction bases catalog.
          cycle_selector.cycle_option := pfc$lowest_cycle;
          pfp$purge (element_paths [rac$correction_base_cat_path]^, cycle_selector, password, ignore_status);
        IFEND;
        IF base_file.path (1, base_file.size) = scratch_file.path (1, scratch_file.size) THEN
          { The scratch file was used as the base, reformat message to show actual path.  This occurs
          { only for version based corrections where the base file is not readable at the current ring.
          osp$set_status_abnormal ('RA', rae$corr_base_checksum_mismatch, '', status);
          osp$append_status_file (osc$status_parameter_delimiter, base_file.path(1, base_file.size),
                status);
        IFEND;
      IFEND;
      RETURN;
    IFEND;

    IF base_file.path (1, base_file.size) =
          scratch_file.path (1, scratch_file.size) THEN
      amp$return (scratch_file.path (1, scratch_file.size), ignore_status);
    IFEND;

    { Purge the correction in the loading cycle.

    cycle_selector.cycle_option := pfc$specific_cycle;
    cycle_selector.cycle_number := rac$loading_cycle;
    pfp$purge (element_paths [rac$installation_catalog_path]^, cycle_selector, password, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    { Move the file from the staging cycle to the loading cycle with the correct storage class.
    { The patch file is in the loading cycle.

    copy_file (corrected_file, patch_file, majority_file_class, ignore_storage_class, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    { Purge the original corrected file from the staging cycle.

    cycle_selector.cycle_option := pfc$specific_cycle;
    cycle_selector.cycle_number := rac$staging_cycle;
    pfp$purge (element_paths [rac$installation_catalog_path]^, cycle_selector, password, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

  PROCEND apply_object_correction;

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

{ PURPOSE:
{   This procedure copies a file to another file, tasking down if
{   user's nominal ring is not in the read bracket of base file.
{   The target file is given ring attributes of the user's nominal ring.
{
{ DESIGN:
{   Since the ring attributes of the base file may prevent it from from
{   being read at this job's current execution ring, we may have to task
{   down to perform the copy.  If this is needed, some SCL commands are
{   "included" which will task down, copy the file, and change its ring
{   attributes to match the current job's nominal ring.
{
{ NOTES:
{   The code in this procedure which gets ring information could/should
{   be put into a procedure which receives a file path and returns
{   parameters indicating the user's current ring, and the read ring for
{   the file.  This would elimimate duplicate code in this routine
{   and GET_PATH_TO_READ_FILE.
{

  PROCEDURE copy_file_at_any_ring
    (    base_file_read_ring: ost$valid_ring;
         base_file: rat$path;
         target_file: rat$path;
     VAR status: ost$status);


    VAR
      base_file_read_ring_str: ost$string,
      caller_id: ost$caller_identifier,
      command_line: string (2500),           {3*512 per path plus extra}
      command_line_length: integer,
      executing_ring_str: ost$string;


    status.normal := TRUE;

    clp$convert_integer_to_string (base_file_read_ring, 10, FALSE, base_file_read_ring_str, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    #CALLER_ID (caller_id);
    clp$convert_integer_to_string (caller_id.ring, 10, FALSE, executing_ring_str, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    { Collect the SCL commands required to task down and copy the base file to the target file.

?? FMT (FORMAT := OFF) ??
    STRINGREP (command_line, command_line_length,
          'task r=', base_file_read_ring_str.value (1, base_file_read_ring_str.size), '; ',
          '  $system.copy_file i=', base_file.path (1, base_file.size),
                   ' o=', target_file.path (1, target_file.size), '; ',
          '  $system.change_file_attributes f=', target_file.path (1, target_file.size),
                   ' ra=', executing_ring_str.value (1, executing_ring_str.size), '; ',
          'taskend');
?? FMT (FORMAT := ON) ??

    clp$include_line (command_line (1, command_line_length), TRUE, osc$null_name, status);

  PROCEND copy_file_at_any_ring;

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

{ PURPOSE:
{   This procedure copies a permanent file to another permanent file
{   placing the file on the requested storage class.
{
{ DESIGN:
{   Copy the base file to the target file, using the mass storage class
{   parameters when determining what device to write the target file.
{   Call another procedure to perform the file copy.  It will
{   take ring attributes into consideration.
{
{ NOTES:
{   If there was a way to know if the desired storage class was
{   also the default, this code would not need to
{   do request_mass_storage every time.
{

  PROCEDURE copy_file
    (    base_file: rat$path;
         target_file: rat$path;
         storage_class: rmt$mass_storage_class;
         ignore_storage_class: boolean;
     VAR status: ost$status);


    VAR
      base_file_read_ring: ost$valid_ring,
      readable_at_executing_ring: boolean,
      return_status: ost$status;


    status.normal := TRUE;

    rmp$request_mass_storage (target_file.path (1, target_file.size), rmc$unspecified_allocation_size,
          rmc$unspecified_file_size, storage_class, rmc$unspecified_vsn, TRUE, status);
    IF (NOT status.normal) AND (NOT ignore_storage_class) THEN
      RETURN;
    IFEND;

    get_file_read_ring (base_file, readable_at_executing_ring, base_file_read_ring, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    IF readable_at_executing_ring THEN
      fsp$copy_file (base_file.path (1, base_file.size), target_file.path (1, target_file.size),
            NIL, NIL, NIL, status);
    ELSE
      copy_file_at_any_ring (base_file_read_ring, base_file, target_file, status);
    IFEND;
    { Don't return until amp$return is performed.

    amp$return (target_file.path (1, target_file.size), return_status);
    IF (status.normal) AND (NOT return_status.normal) AND (NOT ignore_storage_class) THEN
      status := return_status;
    IFEND;

    IF NOT status.normal THEN
      RETURN;
    IFEND;

  PROCEND copy_file;

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

{ PURPOSE:
{   This procedure corrects a file if it needs to be corrected.
{
{ DESIGN:
{   This procedure uses the correction_directives for the file and
{   installation_scheme for the subproduct to determine how to correct
{   the file.
{
{   1) If using a previous correction then call a procedure to delete
{      the correction from the loading cycle.  If the installation scheme
{      is version based, copy the previously corrected file from the
{      active level catalog to the loading cycle.  Verify the checksum of
{      the file.
{
{   2) If using a released file (only occurs for version based corrections),
{      copy the file from the base level catalog.  No validation takes place.
{
{   3) If correcting the file, call a procedure to pply the correction
{      and write the corrected file to the staging cycle, delete the
{      correction in the loading cycle and do a change_catalog_entry to
{      put the corrected file in the loading cycle.  If the base was
{      located in the base level catalog and the correction installation
{      scheme is cycle based, copy the correction base file to the
{      correction_base catalog.
{
{   4) If a file is being replaced, no processing is necessary.
{
{   All files which are left in the loading cycle will be in the
{   majority storage class in order for them to be processed
{   properly during staging.
{
{ NOTES:
{   The subcatalog the files will be written into has been created by
{   correct_subproduct.

  PROCEDURE correct_file
    (    element_p: ^rat$element;
         element_paths: rat$element_paths;
         scratch_file: rat$path;
         installation_scheme: rat$installation_scheme;
         majority_file_class: rmt$mass_storage_class;
         ignore_storage_class: boolean;
     VAR status: ost$status);

    VAR
      file: rat$path,
      release_file: rat$path;

    status.normal := TRUE;

    IF rac$use_previous_correction IN element_p^.correction_directives THEN

      process_previous_correction (element_p, element_paths [rac$installation_catalog_path]^,
            element_paths [rac$active_level_path]^, scratch_file, installation_scheme, majority_file_class,
            ignore_storage_class, status);

    ELSEIF rac$use_release_file IN element_p^.correction_directives THEN

      { True when moving forward uncorrected file in version based subproduct.
      rap$convert_path_to_str (element_paths [rac$base_level_path]^, release_file);
      rap$convert_path_to_str (element_paths [rac$installation_catalog_path]^, file);
      STRINGREP( file.path, file.size, file.path(1, file.size), '.', rac$loading_cycle_str);
      copy_file (release_file, file, majority_file_class, ignore_storage_class, status);

    ELSEIF (rac$use_base_level_catalog IN element_p^.correction_directives) OR
          (rac$use_correction_base_catalog IN element_p^.correction_directives) THEN

      IF element_p^.correction_format = rac$object_library THEN
        apply_object_correction (element_p, element_paths, scratch_file, majority_file_class,
              installation_scheme, ignore_storage_class, status);
      ELSEIF element_p^.correction_format = rac$source_library THEN
        { Do nothing.  Source libraries currently corrected by file replacement.
      ELSE { element_p^.correction_format = rac$replacement }
        { Do nothing.
      IFEND;

    IFEND;

  PROCEND correct_file;

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

{ PURPOSE:
{   This procedure corrects the files belonging to a subproduct.
{
{ DESIGN:
{   Traverse the element list using the standard element list traversal
{   algorithm.
{
{   First, update the array of paths (by making a new array) to represent
{   the current catalog.  Then, process each file and subcatalog in the
{   current catalog.  For a version based correction, attempt to create each
{   subcatalog found since it may not have been created during the loading
{   step when corrections were restored.
{
{ NOTES:
{   This procedure does not distinguish between active and inactive elements
{   as is the case in all other steps.
{

  PROCEDURE correct_subproduct
    (    element_p: ^rat$element;
         element_paths: rat$element_paths;
         scratch_file: rat$path;
         installation_scheme: rat$installation_scheme;
         majority_file_class: rmt$mass_storage_class;
         ignore_storage_class: boolean;
         subproduct_info_seq_p: ^rat$subproduct_info_sequence;
     VAR status: ost$status);


    VAR
      current_element_p: ^rat$element,
      current_element_paths: rat$element_paths,
      first_element_down_p: ^rat$element,
      i: integer,
      ignore_status: ost$status,
      path_index: rat$installation_paths;

    status.normal := TRUE;

    { Create a new array of PF paths which contains path's one larger than
    { each of the path arrays in ELEMENT_PATHS and initialize each path.
    { This array will be used to construct the PF
    { paths for the files and subcatalogs that reside in the current catalog.

    FOR path_index := LOWERBOUND (element_paths) TO UPPERBOUND (element_paths) DO
      IF element_paths [path_index] <> NIL THEN
        PUSH current_element_paths [path_index]: [1 .. UPPERBOUND (element_paths [path_index]^) + 1];
        FOR i := 1 TO UPPERBOUND (element_paths [path_index]^) DO
          current_element_paths [path_index]^ [i] := element_paths [path_index]^ [i];
        FOREND;
      ELSE
        current_element_paths [path_index] := NIL;
      IFEND;
    FOREND;

    { Process the files and subcatalogs at the current catalog level.

    current_element_p := element_p;

    WHILE current_element_p <> NIL DO

      { Add current element name to end of each path.

      FOR path_index := LOWERBOUND (current_element_paths) TO UPPERBOUND (current_element_paths) DO
        IF current_element_paths [path_index] <> NIL THEN
          current_element_paths [path_index]^ [UPPERBOUND (current_element_paths [path_index]^)] :=
                current_element_p^.name;
        IFEND;
      FOREND;

      IF current_element_p^.element_type = rac$file THEN

        correct_file (current_element_p, current_element_paths, scratch_file, installation_scheme,
              majority_file_class, ignore_storage_class, status);

      ELSE {current_element_p^.element_type = rac$catalog}

        IF installation_scheme = rac$version_based THEN
          pfp$define_catalog (current_element_paths [rac$installation_catalog_path]^, ignore_status);
        IFEND;

        IF current_element_p^.element_count <> 0 THEN
          first_element_down_p := #PTR (current_element_p^.first_element_down_p, subproduct_info_seq_p^);

          correct_subproduct (first_element_down_p, current_element_paths, scratch_file, installation_scheme,
                majority_file_class, ignore_storage_class, subproduct_info_seq_p, status);
        IFEND;

      IFEND;
      IF NOT status.normal THEN
        RETURN;
      IFEND;

      current_element_p := #PTR (current_element_p^.next_element_across_p, subproduct_info_seq_p^);
    WHILEND;

  PROCEND correct_subproduct;

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

{ PURPOSE:
{   This procedure creates all the subcatalogs necessary to write a file into
{   the correction bases catalog.
{
{ DESIGN:
{   The parameter to this procedure is the file which will be written into the
{   catalog.  This procedure will create all the subcatalogs necessary to
{   copy the file.
{
{ NOTES:
{   This code is a derivative 'prepare_loading_destination' in the LOAD_PRODUCTS
{   step of INSTALL_SOFTWARE.  Perhaps these could be consolidated.
{

  PROCEDURE create_correction_base_cat_path
    (    file_path: pft$path;
     VAR status: ost$status);


    CONST
      file_name = 1, {The number of elements that file name portion of the full path occupies. }
      first_subcatalog = 1,
      family_user_catalogs = 2; { The number of elements that makeup the family and user catalogs. }

    VAR
      destination_path_p: ^pft$path,
      ignore_status: ost$status,
      number_of_subcatalogs: fst$number_of_path_elements,
      path_sequence_p: ^SEQ ( * ),
      subcatalogs: integer;


    status.normal := TRUE;

    PUSH path_sequence_p: [[REP #SIZE (file_path) OF cell]];
    RESET path_sequence_p;
    NEXT destination_path_p: [1 .. UPPERBOUND (file_path)] IN path_sequence_p;
    destination_path_p^ := file_path;

    number_of_subcatalogs := UPPERBOUND (destination_path_p^) - family_user_catalogs - file_name;

    FOR subcatalogs := first_subcatalog TO number_of_subcatalogs DO

      RESET path_sequence_p;
      NEXT destination_path_p: [1 .. family_user_catalogs + subcatalogs] IN path_sequence_p;

      pfp$define_catalog (destination_path_p^, ignore_status);

    FOREND;

  PROCEND create_correction_base_cat_path;

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

{ PURPOSE:
{   Establishes absolute pointers to the path's for this subproduct.
{ DESIGN:

  PROCEDURE establish_subproduct_paths
    (    processing_record: rat$subp_processing_record;
         processing_seq_p: ^rat$processing_sequence;
     VAR subproduct_paths: rat$element_paths);


    subproduct_paths [rac$installation_catalog_path] := #PTR (processing_record.installation_catalog_rel_p,
          processing_seq_p^);
    subproduct_paths [rac$correction_base_cat_path] := #PTR (processing_record.correction_base_catalog_rel_p,
          processing_seq_p^);
    subproduct_paths [rac$base_level_path] := #PTR (processing_record.base_level_catalog_rel_p,
          processing_seq_p^);
    IF processing_record.active_level_catalog_rel_p <> NIL THEN
      subproduct_paths [rac$active_level_path] := #PTR (processing_record.active_level_catalog_rel_p,
            processing_seq_p^);
    ELSE
      subproduct_paths [rac$active_level_path] := NIL;
    IFEND;

  PROCEND establish_subproduct_paths;

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

{ PURPOSE:
{   This procedure returns a path at which a file can be read.  If necessary
{   the file will be copied to a scratch file with ring attribute set
{   at the currently executing ring.
{
{ DESIGN:

  PROCEDURE get_path_to_read_file
    (    desired_file: rat$path;
         scratch_file: rat$path;
     VAR readable_file: rat$path;
     VAR status: ost$status);


    VAR
      file_read_ring: ost$valid_ring,
      readable_at_executing_ring: boolean;


    status.normal := TRUE;

    get_file_read_ring (desired_file, readable_at_executing_ring, file_read_ring, status);

    IF readable_at_executing_ring THEN
      readable_file := desired_file;
    ELSE
      readable_file := scratch_file;
      copy_file_at_any_ring (file_read_ring, desired_file, readable_file, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;
    IFEND;

  PROCEND get_path_to_read_file;

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

{ PURPOSE:
{   This procedure determines whether or not a file is readable at the ring
{   currently executing in.  A boolean and the value of the read ring are
{   returned.
{
{ DESIGN:
{

  PROCEDURE get_file_read_ring
    (    file: rat$path;
     VAR readable_at_executing_ring: boolean;
     VAR file_read_ring: ost$valid_ring;
     VAR status: ost$status);


    VAR
      caller_id: ost$caller_identifier,
      file_attribute: array [1 .. 1] of amt$get_item,
      ignore_existing_file: boolean,
      ignore_contains_data: boolean,
      ignore_local_file: boolean;


    status.normal := TRUE;
    file_attribute [1].key := amc$ring_attributes;

    amp$get_file_attributes (file.path (1, file.size), file_attribute, ignore_local_file,
          ignore_existing_file, ignore_contains_data, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    #CALLER_ID (caller_id);

    readable_at_executing_ring := (caller_id.ring <= file_attribute [1].ring_attributes.r2);

    file_read_ring := file_attribute [1].ring_attributes.r2;

  PROCEND get_file_read_ring;

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

{ PURPOSE:
{   This procedure prepares the file to be used as the basis for a
{   correction, ie.  place it in the correction base catalog if needed,
{   or copy it to a local file which can be read in the job's current
{   execution ring.  It returns the path to the file which will be used
{   as the correction base.
{
{ DESIGN:
{   If we must get the base file from the base level catalog then copy
{   it to the correction base catalog.  Put the file on a class M device
{   to conserve system critical file space.
{
{   If the base file need not be copied to the correction base catalog (which
{   is the case for version based corrections), and the file is not readable in
{   the job's current ring, copy the file to a $local.$unique file.
{
{ NOTES:
{   When the file is copied to the $local catalog, it is the applier's
{   responsibility to return the file. The file will also be returned by this
{   step's abort handler.
{

  PROCEDURE prepare_correction_base_file
    (    element_p: ^rat$element;
         installation_scheme: rat$installation_scheme;
         correction_base_catalog_file: pft$path;
         base_level_file: pft$path;
         scratch_file: rat$path;
     VAR base_file: rat$path;
     VAR correction_base_catalog_updated: boolean;
     VAR status: ost$status);

    VAR
      actual_checksum: rat$checksum,
      base_file_string: rat$path,
      file_path_to_display: rat$path;

    status.normal := TRUE;

    correction_base_catalog_updated := FALSE;

    IF rac$use_correction_base_catalog IN element_p^.correction_directives THEN

      rap$convert_path_to_str (correction_base_catalog_file, base_file);

    ELSE {rac$use_base_level_catalog IN element_p^.correction_directives}

      IF installation_scheme = rac$cycle_based THEN

        { A copy of the base file will be placed in the correction base catalog
        { for future corrections.

        create_correction_base_cat_path (correction_base_catalog_file, status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;

        rap$convert_path_to_str (correction_base_catalog_file, base_file);
        rap$convert_path_to_str (base_level_file, base_file_string);
        copy_file (base_file_string, base_file, rmc$msc_product_files,
              TRUE { ignore storage class errors } , status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;
        correction_base_catalog_updated := TRUE;

      ELSE { installation scheme = rac$version_based }

        { Guarentee the base file is readable at executing ring.  This
        { may require moving the file to a local copy.

        rap$convert_path_to_str (base_level_file, base_file_string);
        get_path_to_read_file (base_file_string, scratch_file, base_file, status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;
      IFEND;
    IFEND;

  PROCEND prepare_correction_base_file;

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

{ PURPOSE:
{   Validate that that the previous correction file does in fact have the
{   correct contents, copy this file to the loading cycle for a version
{   based correction and get rid of the correction file which was loaded.
{
{ DESIGN:
{   Delete the correction from the loading cycle.
{
{   For a version based correction, call a procedure to copy the previously
{   corrected file to the loading cycle.
{
{   For a cycle based correction, call a procedure to "compute" a path to
{   read the previously corrected file.  (If the file is not readable
{   (due to rings) where it is, it is copied to a local scratch file, the
{   path where the file can be read is returned by the procedure.)
{
{   Checksum the file.  If it doesn't match, delete the file from the loading
{   cycle if it had been copied there and return bad status.
{
{ NOTES:
{

  PROCEDURE process_previous_correction
    (    element_p: ^rat$element;
         file_path: pft$path;
         previously_corrected_path: pft$path;
         scratch_file: rat$path;
         installation_scheme: rat$installation_scheme;
         majority_file_class: rmt$mass_storage_class;
         ignore_storage_class: boolean;
     VAR status: ost$status);

    VAR
      actual_checksum: integer,
      cycle_selector: pft$cycle_selector,
      file_to_checksum: rat$path,
      ignore_status: ost$status,
      password: pft$password,
      previously_corrected_file: rat$path;

    status.normal := TRUE;

    password := ' ';
    cycle_selector.cycle_option := pfc$specific_cycle;
    cycle_selector.cycle_number := rac$loading_cycle;

    { Get rid of correction in the loading cycle.

    pfp$purge (file_path, cycle_selector, password, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    rap$convert_path_to_str (previously_corrected_path, previously_corrected_file);

    IF installation_scheme = rac$version_based THEN
      rap$convert_path_to_str (file_path, file_to_checksum);
      STRINGREP(file_to_checksum.path, file_to_checksum.size, file_to_checksum.path(1, file_to_checksum.size),
            '.', rac$loading_cycle_str );
      copy_file (previously_corrected_file, file_to_checksum, majority_file_class,
            ignore_storage_class, status);
    ELSE { cycle based }
      get_path_to_read_file (previously_corrected_file, scratch_file, file_to_checksum, status);
    IFEND;
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    rap$checksum_file (file_to_checksum.path (1, file_to_checksum.size), actual_checksum, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    IF file_to_checksum.path (1, file_to_checksum.size) = scratch_file.path (1, scratch_file.size) THEN
      amp$return (scratch_file.path (1, scratch_file.size), ignore_status);
    IFEND;

    IF actual_checksum <> element_p^.pre_genc_contents_checksum THEN
      IF installation_scheme = rac$version_based THEN
        { File was copied into the loading cycle, delete it.
        pfp$purge (file_path, cycle_selector, password, ignore_status);
      IFEND;
      { Set status abnormal, subproduct must be installed with override.
      osp$set_status_abnormal ('RA', rae$prev_corr_checksum_mismatch, '', status);
      osp$append_status_file (osc$status_parameter_delimiter, previously_corrected_file.path
            (1, previously_corrected_file.size), status);
      RETURN;
    IFEND;

  PROCEND process_previous_correction;

MODEND ram$correct_products;
