?? RIGHT := 110 ??
?? NEWTITLE := ' NOS/VE Backup/Restore Utilities:  restore_missing_catalogs ', EJECT ??
MODULE pum$restore_missing_catalogs;

{  This contains processing for the RESTORE_MISSING_CATALOGS and
{  the SET_RESTORE_MISSING_CATALOGS subcommands.

?? NEWTITLE := '   Global Declarations', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc osc$nosve_system_set
*copyc osd$integer_limits
*copyc pfe$error_condition_codes
*copyc pue$error_condition_codes
*copyc pft$cycle_count
?? POP ??
*copyc avp$family_administrator
*copyc avp$system_administrator
*copyc clp$get_value
*copyc clp$scan_parameter_list
*copyc osp$set_status_abnormal
*copyc pfp$build_sorted_dfl
*copyc pfp$detach_jobs_catalogs
*copyc pfp$find_cycle_array_extended
*copyc pfp$find_cycle_directory
*copyc pfp$get_family_set
*copyc pfp$put_catalog_media_info
*copyc pfp$put_file_media_info
*copyc pfp$set_restore_status
*copyc pup$build_catalog_header
*copyc pup$build_entry
*copyc pup$close_backup_file
*copyc pup$compare_item_descriptor
*copyc pup$crack_catalog
*copyc pup$crack_file
*copyc pup$crack_resefc_selection
*copyc pup$display_integer
*copyc pup$display_line
*copyc pup$display_restore_totals
*copyc pup$get_item_descriptor
*copyc pup$get_next_record_header
*copyc pup$get_part
*copyc pup$get_summary_status
*copyc pup$initialize_restore_listing
*copyc pup$locate_valid_version
*copyc pup$open_backup_file
*copyc pup$restore_cycle_if_excluded
*copyc pup$set_abnormal_entry_status
*copyc pup$set_restore_subcmd_defaults
*copyc pup$skip_logical_partition
*copyc pup$verify_system_administrator
*copyc pup$write_os_status
*copyc pup$write_path
*copyc puv$cycle_display_selections
*copyc puv$respf_backup_file_version
?? TITLE := '    Global Variables', EJECT ??

  VAR
    puv$catalogs_recovered: ost$non_negative_integers := 0,
    puv$catalogs_recreated: ost$non_negative_integers := 0,
    puv$cycles_entry_recreated: ost$non_negative_integers := 0,
    puv$cycles_fmd_restored: ost$non_negative_integers := 0;

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

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

    VAR
      backup_file_id: put$file_identifier,
      backup_file_lfn: amt$local_file_name,
      catalog_entry: put$entry,
      catalog_path_container: clt$path_container,
      catalog_type: put$entry_type,
      dummy_cycle_selector: pft$cycle_selector,
      local_status: ost$status,
      p_catalog_header: ^put$catalog_header,
      p_catalog_path: ^pft$path,
      restore_excluded_file_cycles: put$restore_data_selections,
      set_name: stt$set_name;

    pup$verify_system_administrator ('RESTORE_MISSING_CATALOGS', NIL, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    crack_restore_missing_catalogs (parameter_list, catalog_type, p_catalog_path, catalog_path_container,
          restore_excluded_file_cycles, backup_file_lfn, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    IF catalog_type = puc$valid_set_entry THEN
      pup$build_entry (osc$nosve_system_set, dummy_cycle_selector, catalog_type, catalog_entry);
      PUSH p_catalog_header: [1 .. 1];
      pup$build_catalog_header (osc$nosve_system_set, NIL, p_catalog_header^);
    ELSE { Family, Master catalog, or Subcatalog }
      pfp$get_family_set (p_catalog_path^ [pfc$family_name_index], set_name, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

      pup$build_entry (p_catalog_path^ [UPPERBOUND (p_catalog_path^)], dummy_cycle_selector, catalog_type,
            catalog_entry);
      PUSH p_catalog_header: [1 .. UPPERBOUND (p_catalog_path^)];
      pup$build_catalog_header (set_name, p_catalog_path, p_catalog_header^);
    IFEND;

    pup$open_backup_file (backup_file_lfn, puc$restore_permanent_files, amc$open_at_boi, backup_file_id,
          status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    pup$set_restore_subcmd_defaults (NOT (avp$system_administrator() OR avp$family_administrator()));

    pfp$build_sorted_dfl (status);
    IF NOT status.normal THEN
      pup$close_backup_file (backup_file_id, local_status);
      RETURN;
    IFEND;


    pup$initialize_restore_listing (' RESTORE MISSING CATALOGS  ', p_catalog_header^, catalog_entry,
          p_catalog_header^.path, dummy_cycle_selector, status);

    puv$catalogs_recovered := 0;
    puv$catalogs_recreated := 0;
    puv$cycles_entry_recreated := 0;
    puv$cycles_fmd_restored := 0;
    restore_missing_catalogs (catalog_entry, p_catalog_header^, restore_excluded_file_cycles, backup_file_id,
          status);
    pup$close_backup_file (backup_file_id, local_status);
    IF status.normal AND (NOT local_status.normal) THEN
      status := local_status;
    IFEND;

    pup$display_restore_totals;
    pup$display_integer ('   NUMBER OF CATALOGS RECOVERED ', puv$catalogs_recovered, local_status);
    pup$display_integer ('   NUMBER OF CATALOGS RECREATED FROM BACKUP ', puv$catalogs_recreated,
          local_status);
    pup$display_integer ('   NUMBER OF CYCLE ENTRIES RECREATED ', puv$cycles_entry_recreated, local_status);
    pup$display_integer ('   NUMBER OF CYCLES DATA RECOVERED ', puv$cycles_fmd_restored, local_status);

    pup$get_summary_status (status);
    pup$write_os_status (status, local_status);

    pfp$detach_jobs_catalogs;

  PROCEND pup$restore_missing_catalogs_cm;
?? TITLE := '  [XDCL] pup$set_restore_missing_catalog ', EJECT ??

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


{  pdt set_restore_missing_cat (
{    operation, o: key start, end = $required
{    status)

?? PUSH (LISTEXT := ON) ??

    VAR
      set_restore_missing_cat: [STATIC, READ, cls$pdt] clt$parameter_descriptor_table :=
            [^set_restore_missing_cat_names, ^set_restore_missing_cat_params];

    VAR
      set_restore_missing_cat_names: [STATIC, READ, cls$pdt_names_and_defaults] array [1 .. 3] of
            clt$parameter_name_descriptor := [['OPERATION', 1], ['O', 1], ['STATUS', 2]];

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

{ OPERATION O }
      [[clc$required], 1, 1, 1, 1, clc$value_range_not_allowed,
            [^set_restore_missing_cat_kv1, clc$keyword_value]],

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

    VAR
      set_restore_missing_cat_kv1: [STATIC, READ, cls$pdt_names_and_defaults] array [1 .. 2] of
            ost$name := ['START', 'END'];

?? POP ??

    VAR
      value: clt$value;

    pup$verify_system_administrator ('SET_RESTORE_MISSING_CATALOGS', NIL, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

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

    clp$get_value ('OPERATION', 1, 1, clc$low, value, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    IF value.name.value = 'START' THEN
      pfp$set_restore_status ({restore_missing_catalogs_done= } FALSE, status);
      IF status.normal THEN
        pup$display_line ('SET_RESTORE_MISSING_CATALOG OPERATION=START', status);
      IFEND;
    ELSE {'END'
      pfp$set_restore_status ({restore_missing_catalogs_done= } TRUE, status);
      IF status.normal THEN
        pup$display_line ('SET_RESTORE_MISSING_CATALOG OPERATION=END', status);
      IFEND;
    IFEND;

  PROCEND pup$set_restore_missing_catalog;
?? TITLE := '    crack_restore_missing_catalogs ', EJECT ??

  PROCEDURE crack_restore_missing_catalogs
    (    parameter_list: clt$parameter_list;
     VAR catalog_type: put$entry_type;
     VAR p_catalog_path: ^pft$path;
     VAR catalog_path_container: clt$path_container;
     VAR restore_excluded_file_cycles: put$restore_data_selections;
     VAR backup_file: amt$local_file_name;
     VAR status: ost$status);



{ PDT restore_missing_cats_pdt (
{  catalog, c: file
{  restore_excluded_file_cycles, refc: list of key none, media_missing mm ..
{     no_data_defined ndd volume_unavailable vu = (media_missing no_data_defined)
{  backup_file, bf: file = $required
{  status)

?? PUSH (LISTEXT := ON) ??

    VAR
      restore_missing_cats_pdt: [STATIC, READ, cls$pdt] clt$parameter_descriptor_table :=
            [^restore_missing_cats_pdt_names, ^restore_missing_cats_pdt_params];

    VAR
      restore_missing_cats_pdt_names: [STATIC, READ, cls$pdt_names_and_defaults] array [1 .. 7] of
            clt$parameter_name_descriptor := [['CATALOG', 1], ['C', 1], ['RESTORE_EXCLUDED_FILE_CYCLES', 2],
            ['REFC', 2], ['BACKUP_FILE', 3], ['BF', 3], ['STATUS', 4]];

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

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

{ RESTORE_EXCLUDED_FILE_CYCLES REFC }
      [[clc$optional_with_default, ^restore_missing_cats_pdt_dv2], 1, clc$max_value_sets, 1, 1,
            clc$value_range_not_allowed, [^restore_missing_cats_pdt_kv2, clc$keyword_value]],

{ BACKUP_FILE BF }
      [[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]]];

    VAR
      restore_missing_cats_pdt_kv2: [STATIC, READ, cls$pdt_names_and_defaults] array [1 .. 7] of
            ost$name := ['NONE', 'MEDIA_MISSING', 'MM', 'NO_DATA_DEFINED', 'NDD', 'VOLUME_UNAVAILABLE', 'VU'];

    VAR
      restore_missing_cats_pdt_dv2: [STATIC, READ, cls$pdt_names_and_defaults] string (31) :=
            '(media_missing n' CAT 'o_data_defined)';

?? POP ??

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

    pup$crack_catalog ('CATALOG', catalog_path_container, p_catalog_path, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    IF (p_catalog_path = NIL) THEN
      { If no CATALOG is specified, default to everything on the backup file.
      catalog_type := puc$valid_set_entry;
    ELSEIF UPPERBOUND (p_catalog_path^) = pfc$family_name_index THEN
      catalog_type := puc$valid_family_entry
    ELSE
      catalog_type := puc$valid_catalog_entry;
    IFEND;

    pup$crack_file ('BACKUP_FILE', backup_file, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    pup$crack_resefc_selection ('RESTORE_EXCLUDED_FILE_CYCLES', restore_excluded_file_cycles, status);

  PROCEND crack_restore_missing_catalogs;

?? TITLE := '  restore_missing_catalogs ', EJECT ??

  PROCEDURE restore_missing_catalogs
    (    catalog_entry: put$entry;
         catalog_header: put$catalog_header;
         restore_excluded_file_cycles: put$restore_data_selections;
     VAR backup_file_id: {Input, Output} put$file_identifier;
     VAR status: ost$status);

    VAR
      anything_found: boolean,
      catalog_child_found: boolean,
      catalog_found: boolean,
      file_position: put$file_position,
      found_entry: put$entry,
      local_status: ost$status,
      p_cycle_array_extended_record: pft$p_info_record,
      p_cycle_directory_array: pft$p_cycle_directory_array,
      p_found_item_description: ^put$backup_item_descriptor,
      record_header: put$backup_file_record_header,
      stored_backup_file_version: put$backup_file_version_name;

    p_cycle_array_extended_record := NIL;
    p_cycle_directory_array := NIL;
    anything_found := FALSE;

  /loop_through_partitions/
    REPEAT
      pup$locate_valid_version (backup_file_id, stored_backup_file_version, file_position, status);
      IF status.normal AND (file_position <> puc$eoi) THEN
        pup$get_next_record_header (backup_file_id, record_header, file_position, status);
        IF status.normal THEN
          IF (record_header.kind = puc$backup_item_identifier) AND (record_header.size >= 1) THEN
            ALLOCATE p_found_item_description: [1 .. record_header.size];
            pup$get_item_descriptor (backup_file_id, p_found_item_description^, file_position, status);
            IF status.normal THEN
              found_entry := p_found_item_description^.pf_utility_entry;
              pup$compare_item_descriptor (catalog_entry, catalog_header, found_entry,
                    p_found_item_description^.catalog_header, catalog_found, catalog_child_found);
              IF catalog_found OR catalog_child_found THEN
                anything_found := TRUE;
                CASE found_entry.entry_type OF
                = puc$valid_set_entry =
                = puc$valid_family_entry, puc$valid_catalog_entry =
                  restore_catalog_media (p_found_item_description^.catalog_header, catalog_found,
                        backup_file_id, file_position, status);
                  IF NOT status.normal AND catalog_found THEN
                    RETURN;
                  IFEND;
                = puc$valid_pf_entry =
                  restore_file_media (p_found_item_description^.catalog_header, p_cycle_array_extended_record,
                        p_cycle_directory_array, backup_file_id, file_position, status);
                = puc$valid_cycle_entry =
                  IF restore_excluded_file_cycles <> $put$restore_data_selections [] THEN
                    pup$restore_cycle_if_excluded (found_entry, p_found_item_description^.catalog_header,
                          p_found_item_description^.catalog_header, found_entry.pf_selector.cycle_selector,
                          found_entry, p_found_item_description^.catalog_header, restore_excluded_file_cycles,
                          p_cycle_array_extended_record, p_cycle_directory_array, backup_file_id,
                          file_position, status);
                  IFEND;
                ELSE
                CASEND;
              IFEND;
            IFEND;
            FREE p_found_item_description;
          ELSE
            osp$set_status_abnormal (puc$pf_utility_id, pue$unexpected_item_requested, ' identifier ',
                  status);
          IFEND;
        IFEND;
        pup$write_os_status (status, local_status);
        status.normal := TRUE;
        IF file_position = puc$mid_partition THEN
          pup$skip_logical_partition (backup_file_id, file_position, status);
        IFEND;
      IFEND;
    UNTIL NOT status.normal OR (file_position = puc$eoi);
    IF status.normal AND NOT anything_found THEN
      pup$set_abnormal_entry_status (catalog_entry, pue$no_restore_no_find, status);
    IFEND;
  PROCEND restore_missing_catalogs;

?? TITLE := '    restore_catalog_media ', EJECT ??

  PROCEDURE restore_catalog_media
    (    catalog_header: put$catalog_header;
         catalog_specified_on_subcommand: boolean;
     VAR backup_file_id: {Input, Output} put$file_identifier;
     VAR file_position: put$file_position;
     VAR status: ost$status);

    VAR
      local_status: ost$status,
      p_catalog_group: pft$p_info_record,
      record_header: put$backup_file_record_header,
      restore_catalog_status: pft$restore_catalog_status,
      transfer_count: amt$file_length;

    {input  catalog item info}
    pup$get_next_record_header (backup_file_id, record_header, file_position, status);
    IF status.normal THEN
      IF (file_position = puc$mid_partition) AND ((record_header.kind = puc$backup_family_info) OR
            (record_header.kind = puc$backup_catalog_info)) AND (record_header.size > 0) THEN
        PUSH p_catalog_group: [[REP record_header.size OF cell]];
        pup$get_part (backup_file_id, p_catalog_group, #SIZE (p_catalog_group^), file_position,
              transfer_count, status);
      ELSE
        osp$set_status_abnormal (puc$pf_utility_id, pue$unusable_restore_file, ' catalog info ', status);
      IFEND;
    IFEND;

    {put catalog item info }
    IF status.normal THEN
      pfp$put_catalog_media_info (catalog_header.path, p_catalog_group, catalog_header.set_name,
          restore_catalog_status, status);
      IF status.normal THEN
        CASE restore_catalog_status OF
        = pfc$catalog_already_exists =
          { Display nothing.
        = pfc$catalog_recreated =
          puv$catalogs_recreated := puv$catalogs_recreated + 1;
          pup$write_path (catalog_header.path, local_status);
          pup$display_line ('                     CATALOG RECREATED FROM BACKUP', local_status);
        = pfc$catalog_recovered =
          puv$catalogs_recovered := puv$catalogs_recovered + 1;
          pup$write_path (catalog_header.path, local_status);
          pup$display_line ('                     CATALOG RECOVERED', local_status);
        ELSE
        CASEND;
      ELSE
        IF catalog_specified_on_subcommand THEN
          { If there was an error on the catalog specified on the subcommand
          { return the status, so the user sees it.
        ELSE
          CASE status.condition OF
          = pfe$last_name_not_subcatalog =
            { A file already exists of the same name in the catalog.
            status.normal := TRUE;
          = pfe$unknown_last_subcatalog =
            { The parent catalog was not recreated, so the catalog must have
            { been deleted since the backup.
            status.normal := TRUE;
          ELSE
          CASEND;
        IFEND;
      IFEND;
    IFEND;
    IF NOT status.normal THEN
      pup$write_path (catalog_header.path, local_status);
      pup$write_os_status (status, local_status);
    IFEND;
  PROCEND restore_catalog_media;
?? TITLE := '    restore_file_media ', EJECT ??

  PROCEDURE restore_file_media
    (    catalog_header: put$catalog_header;
     VAR p_cycle_array_extended_record: pft$p_info_record;
     VAR p_cycle_directory_array: pft$p_cycle_directory_array;
     VAR backup_file_id: put$file_identifier;
     VAR file_position: put$file_position;
     VAR status: ost$status);

    VAR
      backup_file_version: pft$backup_file_version,
      cycles_fmd_recovered: pft$cycle_count,
      cycle_entries_recreated: pft$cycle_count,
      file_display_string: string (80),
      file_display_string_length: integer,
      file_entry_recreated: boolean,
      local_status: ost$status,
      p_file_group: pft$p_info_record,
      record_header: put$backup_file_record_header,
      transfer_count: amt$file_length;

    {input item info}
    pup$get_next_record_header (backup_file_id, record_header, file_position, status);
    IF status.normal THEN
      IF (file_position = puc$mid_partition) AND (record_header.kind = puc$backup_file_info) AND
            (record_header.size > 0) THEN
        PUSH p_file_group: [[REP record_header.size OF cell]];
        pup$get_part (backup_file_id, p_file_group, #SIZE (p_file_group^), file_position, transfer_count,
              status);
      ELSE
        osp$set_status_abnormal (puc$pf_utility_id, pue$unusable_restore_file, ' file info ', status);
      IFEND;
    IFEND;

{put item info }
    IF status.normal THEN
      IF puv$respf_backup_file_version = puc$backup_file_version_1 THEN
        backup_file_version := pfc$backup_file_version_1;
      ELSEIF puv$respf_backup_file_version = puc$backup_file_version_2 THEN
        backup_file_version := pfc$backup_file_version_2;
      IFEND;
    pfp$put_file_media_info (catalog_header.path, p_file_group, catalog_header.set_name,
            backup_file_version, file_entry_recreated, cycles_fmd_recovered, cycle_entries_recreated, status);
    IFEND;

    IF status.normal THEN
      IF puc$cdo_alternate_storage IN puv$cycle_display_selections THEN
        pfp$find_cycle_array_extended (p_file_group, p_cycle_array_extended_record, status);
        IF status.normal THEN
          pfp$find_cycle_directory (p_cycle_array_extended_record, p_cycle_directory_array, status);
        IFEND;
        IF NOT status.normal THEN
          p_cycle_array_extended_record := NIL;
        IFEND;
      IFEND;
      IF file_entry_recreated OR (cycles_fmd_recovered > 0) OR (cycle_entries_recreated > 0) THEN
        puv$cycles_entry_recreated := puv$cycles_entry_recreated + cycle_entries_recreated;
        puv$cycles_fmd_restored := puv$cycles_fmd_restored + cycles_fmd_recovered;
        pup$write_path (catalog_header.path, local_status);
        file_display_string_length := 0;
        file_display_string := ' ';
        IF file_entry_recreated THEN
          STRINGREP (file_display_string, file_display_string_length, '   FILE RECREATED ---');
        ELSE
          file_display_string_length := 2;
        IFEND;
        STRINGREP (file_display_string, file_display_string_length,
              file_display_string (1, file_display_string_length), '-- CYCLES RECREATED ',
              cycle_entries_recreated, ',');
        STRINGREP (file_display_string, file_display_string_length,
              file_display_string (1, file_display_string_length), '-- CYCLES RECOVERED ',
              cycles_fmd_recovered);
        pup$display_line (file_display_string (1, file_display_string_length), local_status);
      IFEND;
    ELSE
      p_cycle_array_extended_record := NIL;
      CASE status.condition OF
      = pfe$name_not_permanent_file =
        { A catalog of the same name has been created since the backup.
        status.normal := TRUE;
      = pfe$unknown_permanent_file =
        { The parent catalog was not recreated, so we did not create the file.
        status.normal := TRUE;
      ELSE
      CASEND;
    IFEND;
    IF NOT status.normal THEN
      pup$write_path (catalog_header.path, local_status);
      pup$write_os_status (status, local_status);
    IFEND;
  PROCEND restore_file_media;

MODEND pum$restore_missing_catalogs;
