?? RIGHT := 110 ??
*copyc osd$default_pragmats
?? NEWTITLE := ' NOS/VE Backup/Restore Utilities:  backup_catalog ', EJECT ??
MODULE pum$backup_catalog;
{PURPOSE:
{     This module contains the procedures which produce a BACKUP copy
{  of a specified catalog as well as a BACKUP copy of each subcatalog,
{  file, and cycle which is registered in the subsequent tree structure
{  of the specified catalog.
?? NEWTITLE := '   Global Declarations', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc pud$backup_file
*copyc pud$hierarchy_list
*copyc clt$file
*copyc fst$path
*copyc fst$path_size
*copyc ost$caller_identifier
*copyc ost$name
*copyc pft$cycle_reservation_criteria
*copyc put$file_identifier
*copyc rmt$recorded_vsn
*copyc stt$set_name
?? POP ??
*copyc avp$system_administrator
*copyc clp$scan_parameter_list
*copyc mmp$create_scratch_segment
*copyc mmp$delete_scratch_segment
*copyc ofp$display_status_message
*copyc osp$append_status_parameter
*copyc osp$disestablish_cond_handler
*copyc osp$establish_block_exit_hndlr
*copyc osp$set_status_abnormal
*copyc pfp$convert_pf_path_to_fs_path
*copyc pfp$convert_pft$path_to_string
*copyc pfp$detach_reserved_cycles
*copyc pfp$find_direct_info_record
*copyc pfp$find_directory_array
*copyc pfp$find_next_info_record
*copyc pfp$get_family_set
*copyc pfp$get_item_info
*copyc pfp$get_multi_item_info
*copyc pfp$get_reserved_item_info
*copyc pup$abort_output
*copyc pup$backup_family_request
*copyc pup$backup_file
*copyc pup$build_catalog_header
*copyc pup$build_entry
*copyc pup$build_hierarchy_list
*copyc pup$build_new_catalog_header
*copyc pup$check_if_item_excluded
*copyc pup$check_if_subitem_excluded
*copyc pup$crack_catalog
*copyc pup$display_backup_output_total
*copyc pup$display_excluded_item
*copyc pup$excluded_highest_cycles
*copyc pup$get_status_severity
*copyc pup$get_summary_status
*copyc pup$initialize_backup_listing
*copyc pup$output_catalog
*copyc pup$verify_catalog_path
*copyc pup$verify_family_administrator
*copyc pup$write_catalog_header
*copyc pup$write_os_status
*copyc pup$write_status_to_listing
*copyc puv$backup_criteria
*copyc puv$backup_file_id
*copyc puv$backup_information
*copyc puv$bacpf_backup_file_version
*copyc puv$exclude_site_backup_options
*copyc puv$global_backup_file_id
*copyc puv$include_archive_information
*copyc puv$include_data_options
*copyc puv$include_exceptions
*copyc puv$include_volumes_option
*copyc puv$maximum_cycle_size
*copyc puv$minimum_cycle_size
*copyc puv$p_included_volumes
*copyc puv$trace_selected
?? TITLE := '    Global Variables', EJECT ??
?? TITLE := '    [XDCL] pup$backup_catalog ', EJECT ??

  PROCEDURE [XDCL] pup$backup_catalog (catalog_entry: put$entry;
        pf_utility_catalog_header: put$catalog_header;
        catalog_item_info: pft$p_info_record;
        pf_utility_hierarchy_list: put$hierarchy_list;
    VAR pf_backup_file_id: put$file_identifier;
    VAR status: ost$status);
{PURPOSE:
{     this procedure produces a BACKUP copy of a specified catalog
{  as well as each file and cycle registered in the catalog.
{NOTE:
{     recursion is used in this procedure to process each subcatalog
{  registered in the specified catalog.

    VAR
      catalog_content_info: amt$segment_pointer,
      catalog_excluded: boolean,
      catalog_info: put$backup_item_info,
      cycle_selector: pft$cycle_selector,
      file_info_selections: pft$file_info_selections,
      fs_path: fst$path,
      fs_path_size: fst$path_size,
      group: pft$group,
      i: put$half_integer,
      ignore_status: ost$status,
      local_status: ost$status,
      message_length: integer,
      message_string: string (ofc$max_display_message),
      next_catalog_entry: put$entry,
      p_body: pft$p_info,
      p_catalog_directory: pft$p_directory_array,
      p_cycle_reservation_criteria: ^pft$cycle_reservation_criteria,
      p_file_description: pft$p_file_description,
      p_info_record: pft$p_info_record,
      p_item_record: pft$p_info_record,
      p_new_catalog_header: ^put$catalog_header,
      path_string: ost$string,
      permanent_file_excluded: boolean,
      pf_entry: put$entry,
      recorded_vsn: rmt$recorded_vsn,
      subitem_excluded: boolean;

    CONST
      for_catalog_string = 'for catalog',
      message_prefix = 'Backing up ',
      message_prefix_length = 11;

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

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

      clean_up;

    PROCEND abort_handler;

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

    PROCEDURE clean_up;

      IF p_cycle_reservation_criteria <> NIL THEN
        pfp$detach_reserved_cycles (local_status);
        p_cycle_reservation_criteria := NIL;
        #SPOIL (p_cycle_reservation_criteria);
      IFEND;

      IF catalog_content_info.sequence_pointer <> NIL THEN
        mmp$delete_scratch_segment (catalog_content_info, local_status);
        catalog_content_info.sequence_pointer := NIL;
        #SPOIL (catalog_content_info.sequence_pointer);
      IFEND;

    PROCEND clean_up;

?? OLDTITLE, EJECT ??

    status.normal := TRUE;
    local_status.normal := TRUE;
    p_cycle_reservation_criteria := NIL;
    catalog_content_info.kind := amc$sequence_pointer;
    catalog_content_info.sequence_pointer := NIL;
    #SPOIL (catalog_content_info, p_cycle_reservation_criteria);
    osp$establish_block_exit_hndlr (^abort_handler);

{
{ OUTPUT CATALOG ITEM
{

    pfp$convert_pft$path_to_string (pf_utility_catalog_header.path, path_string);

    message_string (1, ofc$max_display_message) := message_prefix;
    IF (path_string.size + message_prefix_length) > ofc$max_display_message THEN
      message_string (message_prefix_length + 1, ofc$max_display_message - message_prefix_length) :=
            path_string.value (1, ofc$max_display_message - message_prefix_length);
      message_string (ofc$max_display_message, 1) := '>';
    ELSE
      message_string (message_prefix_length + 1, path_string.size) := path_string.value (1, path_string.size);
    IFEND;

    ofp$display_status_message (message_string (1, ofc$max_display_message), ignore_status);

    pup$write_catalog_header (pf_utility_catalog_header, ignore_status);

    mmp$create_scratch_segment (amc$sequence_pointer, mmc$as_random, catalog_content_info, status);
    IF status.normal THEN
      IF puv$bacpf_backup_file_version = puc$backup_file_version_1 THEN
        IF puv$include_archive_information THEN
          file_info_selections := - $pft$file_info_selections [pfc$file_cycles_version_2];
        ELSE
          file_info_selections :=
                - $pft$file_info_selections [pfc$archive_descriptors, pfc$file_cycles_version_2];
        IFEND;
      ELSEIF puv$bacpf_backup_file_version = puc$backup_file_version_2 THEN
        IF puv$include_archive_information THEN
          file_info_selections := - $pft$file_info_selections [pfc$file_cycles];
        ELSE
          file_info_selections :=
                - $pft$file_info_selections [pfc$archive_descriptors, pfc$file_cycles];
        IFEND;
      IFEND;

      IF avp$system_administrator () AND (puv$bacpf_backup_file_version = puc$backup_file_version_2) AND
            puv$include_exceptions THEN
        PUSH p_cycle_reservation_criteria;
        build_cycle_reservation_crit (p_cycle_reservation_criteria);
      IFEND;

      RESET catalog_content_info.sequence_pointer;
      group.group_type := pfc$public;

      pup$check_if_subitem_excluded (pf_utility_catalog_header, subitem_excluded);
      IF subitem_excluded THEN
        pfp$get_multi_item_info (pf_utility_catalog_header.path, group, - $pft$catalog_info_selections [],
              file_info_selections, catalog_content_info.sequence_pointer, status);
      ELSE
        pfp$get_reserved_item_info (pf_utility_catalog_header.path, group, - $pft$catalog_info_selections [],
              file_info_selections, p_cycle_reservation_criteria, catalog_content_info.sequence_pointer,
              status);
      IFEND;
      IF status.normal THEN
        RESET catalog_content_info.sequence_pointer;
        pfp$find_next_info_record (catalog_content_info.sequence_pointer, p_info_record, status);
        IF status.normal THEN
          catalog_info.item_type := puc$backup_item_catalog_info;
          catalog_info.catalog_item_info := catalog_item_info {single item info } ;
          pup$output_catalog (catalog_entry, pf_utility_catalog_header, catalog_info,
                pf_utility_hierarchy_list, pf_backup_file_id, status);
        IFEND;
        IF NOT status.normal THEN
          pup$abort_output (catalog_entry, pf_backup_file_id, status, local_status);
        IFEND;
      IFEND;
    IFEND;

{
    IF status.normal THEN
      pfp$find_directory_array (p_info_record, p_catalog_directory, status);
      IF status.normal AND (p_catalog_directory <> NIL) THEN
        PUSH p_new_catalog_header: [1 .. (UPPERBOUND (pf_utility_catalog_header.path) + 1)];
{
{
{        BACKUP SUB FILES
        FOR i := LOWERBOUND (p_catalog_directory^) TO UPPERBOUND (p_catalog_directory^) DO
          CASE p_catalog_directory^ [i].name_type OF
          = pfc$file_name =
            pup$build_entry (p_catalog_directory^ [i].name, cycle_selector, puc$valid_pf_entry, pf_entry);
            p_body := ^p_info_record^.body;
            pfp$find_direct_info_record (p_body, p_catalog_directory^ [i].info_offset, p_item_record, status);
            IF status.normal THEN
              pup$build_new_catalog_header (pf_entry, pf_utility_catalog_header, p_new_catalog_header^);
              pup$check_if_item_excluded (pf_entry, p_new_catalog_header^, permanent_file_excluded);
              IF permanent_file_excluded THEN
                pup$display_excluded_item (pf_entry, p_new_catalog_header^, status);
              ELSE
                pup$backup_file (pf_entry, {password_provided =} FALSE, osc$null_name, p_new_catalog_header^,
                      pf_utility_hierarchy_list, pf_backup_file_id, p_item_record, status);
              IFEND;
            IFEND;
            pup$write_status_to_listing (pf_entry, status, local_status);
            IF NOT puv$global_backup_file_id.backup_file_open THEN
              {
              { pup$backup_catalog encountered an error writing to the backup_file and closed it.
              { Return the abnormal status to the caller.
              {
              RETURN;
            IFEND;
            status.normal := TRUE;
          = pfc$catalog_name =
          ELSE
          CASEND;
        FOREND;

        IF p_cycle_reservation_criteria <> NIL THEN
          pfp$detach_reserved_cycles (status);
          p_cycle_reservation_criteria := NIL;
          #SPOIL (p_cycle_reservation_criteria);
        IFEND;
{
{
{        BACKUP SUB CATALOGS
        FOR i := LOWERBOUND (p_catalog_directory^) TO UPPERBOUND (p_catalog_directory^) DO
          CASE p_catalog_directory^ [i].name_type OF
          = pfc$catalog_name =
            pup$build_entry (p_catalog_directory^ [i].name, cycle_selector, puc$valid_catalog_entry,
                  next_catalog_entry);
            pup$build_new_catalog_header (next_catalog_entry, pf_utility_catalog_header,
                  p_new_catalog_header^);
            pup$check_if_item_excluded (next_catalog_entry, p_new_catalog_header^, catalog_excluded);
            IF catalog_excluded THEN
              pup$display_excluded_item (next_catalog_entry, p_new_catalog_header^, status);
            ELSE
              p_body := ^p_info_record^.body;
              pfp$find_direct_info_record (p_body, p_catalog_directory^ [i].info_offset, p_item_record,
                    status);
              IF status.normal THEN
                pup$backup_catalog (next_catalog_entry, p_new_catalog_header^, p_item_record,
                      pf_utility_hierarchy_list, pf_backup_file_id, status);
              IFEND;

              IF NOT status.normal THEN
                IF status.condition = pfe$catalog_volume_unavailable THEN
                  recorded_vsn := status.text.value (2, status.text.size - 1);
                  pfp$convert_pf_path_to_fs_path (p_new_catalog_header^.path, fs_path, fs_path_size);
                  osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$catalog_volume_unavailable,
                        recorded_vsn, status);
                  osp$append_status_parameter (osc$status_parameter_delimiter, for_catalog_string, status);
                  osp$append_status_parameter (osc$status_parameter_delimiter, fs_path (1, fs_path_size),
                        status);
                ELSEIF status.condition = pfe$catalog_volume_not_online THEN
                  recorded_vsn := status.text.value (2, status.text.size - 1);
                  pfp$convert_pf_path_to_fs_path (p_new_catalog_header^.path, fs_path, fs_path_size);
                  osp$set_status_abnormal (pfc$permanent_file_manager_id, pfe$catalog_volume_not_online,
                        recorded_vsn, status);
                  osp$append_status_parameter (osc$status_parameter_delimiter, for_catalog_string, status);
                  osp$append_status_parameter (osc$status_parameter_delimiter, fs_path (1, fs_path_size),
                        status);
                IFEND;
                pup$write_status_to_listing (next_catalog_entry, status, local_status);
              IFEND;

              IF NOT puv$global_backup_file_id.backup_file_open THEN
                {
                { pup$backup_catalog encountered an error writing to the backup_file and closed it.
                { Return the abnormal status to the caller.
                {
                RETURN;
              IFEND;
              status.normal := TRUE;
            IFEND;
          = pfc$file_name =
          ELSE
          CASEND;
        FOREND;
      IFEND;
    IFEND;

    clean_up;
    osp$disestablish_cond_handler;

  PROCEND pup$backup_catalog;

?? TITLE := '    [XDCL] pup$backup_catalog_command ', EJECT ??

  PROCEDURE [XDCL] pup$backup_catalog_command (parameter_list: clt$parameter_list;
    VAR status: ost$status);

    VAR
      local_status: ost$status,
      p_path: ^pft$path,
      path_container: clt$path_container,
      set_name: stt$set_name;

    display (' entering pup$backup_catalog_command');
    IF puv$trace_selected THEN
      IF puc$include_unreleasable_data IN puv$include_data_options THEN
        display ('   unreleasable data included');
      IFEND;
      IF puc$include_releasable_data IN puv$include_data_options THEN
        display ('   releasable data included');
      IFEND;
      IF puc$include_offline_data IN puv$include_data_options THEN
        display ('   offline data included');
      IFEND;
    IFEND;

    crack_backup_catalog (parameter_list, path_container, p_path, status);
    IF status.normal THEN
      IF UPPERBOUND(p_path^) = pfc$family_name_index THEN
        pup$verify_family_administrator (' BACKUP_CATALOG', p_path^[pfc$family_name_index],
              status);
        IF status.normal THEN
          pfp$get_family_set (p_path^ [pfc$family_name_index], set_name, status);
          IF status.normal THEN
            pup$backup_family_request (set_name, p_path^[pfc$family_name_index], puv$backup_file_id, status);
          IFEND;
        ELSE
          osp$append_status_parameter (osc$status_parameter_delimiter, ' to backup a family',
                status);
        IFEND;
      ELSE
        pup$backup_catalog_request (p_path^, puv$backup_file_id, status);
      IFEND;
    IFEND;
  PROCEND pup$backup_catalog_command;

?? TITLE := '    pup$backup_catalog_request ', EJECT ??

  PROCEDURE pup$backup_catalog_request (catalog_path: pft$path;
    VAR pf_backup_file_id: put$file_identifier;
    VAR status: ost$status);

    VAR
      catalog_entry: put$entry,
      catalog_item_info: amt$segment_pointer,
      dummy_cycle_selector: pft$cycle_selector,
      group: pft$group,
      i: put$half_integer,
      local_status: ost$status,
      p_body: pft$p_info,
      p_catalog_directory: pft$p_directory_array,
      p_hierarchy_list: ^put$hierarchy_list,
      p_info_record: pft$p_info_record,
      p_item_record: pft$p_info_record,
      set_name: stt$set_name;

    status.normal := TRUE;
    local_status.normal := TRUE;

    pfp$get_family_set (catalog_path [pfc$family_name_index], set_name, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    pup$build_entry (catalog_path [UPPERBOUND (catalog_path)], dummy_cycle_selector, puc$valid_catalog_entry,
          catalog_entry);
    PUSH p_hierarchy_list: [1 .. UPPERBOUND (catalog_path)];
    pup$build_catalog_header (set_name, ^catalog_path, p_hierarchy_list^.catalog_header);
    pup$build_hierarchy_list (catalog_entry, p_hierarchy_list^.catalog_header, p_hierarchy_list^, status);
    IF status.normal THEN
      pup$verify_catalog_path (catalog_path, status);
      IF status.normal THEN
        mmp$create_scratch_segment (amc$sequence_pointer, mmc$as_random, catalog_item_info, status);
        IF status.normal THEN
          RESET catalog_item_info.sequence_pointer;
          group.group_type := pfc$public;
          pfp$get_item_info (catalog_path, group, - $pft$catalog_info_selections [], $pft$file_info_selections
                [], catalog_item_info.sequence_pointer, status);
          IF status.normal THEN
            RESET catalog_item_info.sequence_pointer;
            pfp$find_next_info_record (catalog_item_info.sequence_pointer, p_info_record, status);
            IF status.normal THEN
              pfp$find_directory_array (p_info_record, p_catalog_directory, status);
              IF status.normal AND (p_catalog_directory <> NIL) THEN
                p_body := ^p_info_record^.body;
                pfp$find_direct_info_record (p_body, p_catalog_directory^ [LOWERBOUND (p_catalog_directory^)].
                      info_offset, p_item_record, status);
                IF status.normal THEN
                  pup$initialize_backup_listing (p_hierarchy_list^, pf_backup_file_id, puv$backup_information,
                        status);
                  IF status.normal THEN
                    pup$backup_catalog (catalog_entry, p_hierarchy_list^.catalog_header, p_item_record,
                          p_hierarchy_list^, pf_backup_file_id, status);
                  IFEND;
                  pup$display_backup_output_total;
                  pup$get_summary_status (status);
                  pup$write_os_status (status, local_status);
                IFEND;
              IFEND;
            IFEND;
          IFEND;
          mmp$delete_scratch_segment (catalog_item_info, local_status);
        IFEND;
      IFEND;
    IFEND;
  PROCEND pup$backup_catalog_request;

?? TITLE := '    build_cycle_reservation_crit ', EJECT ??

  PROCEDURE build_cycle_reservation_crit
    (    p_cycle_reservation_criteria: ^pft$cycle_reservation_criteria);

    VAR
      caller_id: ost$caller_identifier;

    #CALLER_ID (caller_id);

    p_cycle_reservation_criteria^.date_selection_criteria := puv$backup_criteria;
    p_cycle_reservation_criteria^.maximum_cycle_size := puv$maximum_cycle_size;
    p_cycle_reservation_criteria^.minimum_cycle_size := puv$minimum_cycle_size;

    p_cycle_reservation_criteria^.p_volume_list := puv$p_included_volumes;
    p_cycle_reservation_criteria^.include_volumes_option := puv$include_volumes_option;
    p_cycle_reservation_criteria^.exclude_highest_cycles := pup$excluded_highest_cycles ();
    p_cycle_reservation_criteria^.exclude_site_backup_options := puv$exclude_site_backup_options;
    p_cycle_reservation_criteria^.validation_ring := caller_id.ring;

  PROCEND build_cycle_reservation_crit;

?? TITLE := '    crack_backup_catalog ', EJECT ??

  PROCEDURE crack_backup_catalog (parameter_list: clt$parameter_list;
    VAR path_container: clt$path_container;
    VAR p_path: ^pft$path;
    VAR status: ost$status);

{ pdt backup_catalog_pdt (
{ catalog,c:file=$required
{ status)

?? PUSH (LISTEXT := ON) ??

    VAR
      backup_catalog_pdt: [STATIC, READ, cls$pdt] clt$parameter_descriptor_table :=
        [^backup_catalog_pdt_names, ^backup_catalog_pdt_params];

    VAR
      backup_catalog_pdt_names: [STATIC, READ, cls$pdt_names_and_defaults] array [1 .. 3] of
        clt$parameter_name_descriptor := [['CATALOG', 1], ['C', 1], ['STATUS', 2]];

    VAR
      backup_catalog_pdt_params: [STATIC, READ, cls$pdt_parameters] array [1 .. 2] of clt$parameter_descriptor
        := [

{ CATALOG C }
      [[clc$required], 1, 1, 1, 1, clc$value_range_not_allowed, [NIL, clc$file_value]],

{ STATUS }
      [[clc$optional], 1, 1, 1, 1, clc$value_range_not_allowed, [NIL, clc$variable_reference,
        clc$array_not_allowed, clc$status_value]]];

?? POP ??



    clp$scan_parameter_list (parameter_list, backup_catalog_pdt, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    pup$crack_catalog ('CATALOG', path_container, p_path, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
  PROCEND crack_backup_catalog;
MODEND pum$backup_catalog;
