?? RIGHT := 110 ??
?? NEWTITLE := 'PACKAGE_SOFTWARE Utility: READ_TAILORED_FILE Subcommand.' ??
MODULE ram$read_tailored_file;

{ PURPOSE:
{   This module contains the command interface that reads a tailored file.
{
{ DESIGN:
{   The compiled module resides in RAF$LIBRARY.
{
{ NOTES:
{   This module makes extensive use of existing data structures and
{   procedures that are part of the tailored release utility INSTALL
{   SOFTWARE.  A related procedure in INSTALL_SOFTWARE, INSTALL_PRODUCT,
{   was used as a basis to write this module.  Many of the same steps
{   are performed in both modules to read a product tape and copy the
{   contents to some catalog structure.
{

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc rac$control_job_identifier
*copyc rac$sif_file_name
*copyc rac$special_product_designators
*copyc rae$install_software_cc
*copyc rae$package_software_cc
*copyc rat$installation_control_record
?? POP ??
*copyc clp$evaluate_parameters
*copyc clp$trimmed_string_size
*copyc fsp$close_file
*copyc mmp$create_scratch_segment
*copyc mmp$delete_scratch_segment
*copyc osp$append_status_file
*copyc osp$append_status_parameter
*copyc osp$disestablish_cond_handler
*copyc osp$establish_block_exit_hndlr
*copyc osp$generate_error_message
*copyc osp$set_status_abnormal
*copyc pfp$convert_string_to_fs_path
*copyc rap$convert_path_to_pf_format
*copyc rap$convert_path_to_str
*copyc rap$init_processing_seq
*copyc rap$load_products
*copyc rap$verify_catalog_exists
*copyc rap$verify_subproduct_interface
*copyc rap$write_file_from_memory
*copyc rav$installation_defaults
*copyc rav$subproduct_type
?? OLDTITLE, NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

  TYPE
    rat#subprod_qualifier_catalogs = record
      kind: rat#qualifier_catalogs_kinds,
      user_specified: rat$path,
    recend;

  TYPE
    rat#qualifier_catalogs_kinds = (rac#none, rac#level_and_type, rac#user_specified);

?? OLDTITLE, NEWTITLE := '[XDCL] rap$read_tailored_file', EJECT ??

{ PURPOSE:
{   This command interface reads a file or tape generated by the tailored
{   release process, and re-creates the subproducts' PACS catalogs, with
{   their subproduct information files as they would appear at the time
{   the tailored file or tape was generated.
{
{ DESIGN:
{   The processing of any read request is controlled by a
{   reading control record.  The reading control record contains
{   or has access to all pertainent information (including direct access to
{   information in the packing list).
{
{   The reading control record contains pointers to the packing list,
{   the processing sequence and the job status record (in the processing
{   summary file).
{
{   The initial setup prior to performing the reading is:
{
{     1.  Separate the packing list value, entered as a parameter to this
{         routine, into two parts: the part representing the path to the
{         packing list, and the other representing the actual name of the
{         packing list.  This is done in order to interface with existing
{         install software procedures which require this format.
{
{     2.  Initialize the memory sequence for the reading control record,
{         and create a scratch segment required by load products.
{
{     3.  Open the packing list, and fill the reading control record
{         with values from the packing list.
{
{     4.  Perform necessary initialization steps.  This is based on the
{         PRODUCT parameter which allows the caller to specify a subset
{         of subproducts to read from the order.
{
{     When reading of the file/tape begins, the following steps occur:
{
{     5.  Load the subproducts from the file/tape into the catalog path
{         specified by the CATALOG parameter and the
{         SUBPRODUCT_QUALIFIER_CATALOGS (SQC) parameter.  The SQC parameter
{         further defines the catalog structure into which the products are
{         loaded.
{
{     6.  Re-create a subproduct_information_file (SIF) for each subproduct
{         loaded, and write this permanent file into the PACS catalog
{         structure just loaded.
{
{     7.  Finally, the packing list is closed, and the reading control
{         sequence and scratch segment are deleted.
{
{ NOTES:
{   This command interface utilizes existing routines that are part of the
{   install software utility.  As a result, several coding practices are
{   present which accomodate this fact.
{
{   The scratch segment is used for the processing sequence and is created
{   in RAP$INIT_PROCESSING_SEQ.
{

  PROCEDURE [XDCL] rap$read_tailored_file
    (    parameter_list: clt$parameter_list;
     VAR status: ost$status);


{ PROCEDURE reatf_pdt (
{   packing_list, pl: file = $required
{   catalog, c: file = $required
{   subproduct_qualifier_catalogs, sqc: any of
{       key
{         none, level_and_type
{       keyend
{       list of name
{     anyend = level_and_type
{   product, products, p: any of
{       key
{         all
{       keyend
{       list of name
{     anyend = all
{   status
{   )

?? PUSH (LISTEXT := ON) ??
?? FMT (FORMAT := OFF) ??

  VAR
    pdt: [STATIC, READ, cls$declaration_section] record
      header: clt$pdt_header,
      names: array [1 .. 10] of clt$pdt_parameter_name,
      parameters: array [1 .. 5] of clt$pdt_parameter,
      type1: record
        header: clt$type_specification_header,
      recend,
      type2: record
        header: clt$type_specification_header,
      recend,
      type3: record
        header: clt$type_specification_header,
        qualifier: clt$union_type_qualifier,
        type_size_1: clt$type_specification_size,
        element_type_spec_1: record
          header: clt$type_specification_header,
          qualifier: clt$keyword_type_qualifier,
          keyword_specs: array [1 .. 2] of clt$keyword_specification,
        recend,
        type_size_2: clt$type_specification_size,
        element_type_spec_2: record
          header: clt$type_specification_header,
          qualifier: clt$list_type_qualifier,
          element_type_spec: record
            header: clt$type_specification_header,
            qualifier: clt$name_type_qualifier,
          recend,
        recend,
        default_value: string (14),
      recend,
      type4: record
        header: clt$type_specification_header,
        qualifier: clt$union_type_qualifier,
        type_size_1: clt$type_specification_size,
        element_type_spec_1: record
          header: clt$type_specification_header,
          qualifier: clt$keyword_type_qualifier,
          keyword_specs: array [1 .. 1] of clt$keyword_specification,
        recend,
        type_size_2: clt$type_specification_size,
        element_type_spec_2: record
          header: clt$type_specification_header,
          qualifier: clt$list_type_qualifier,
          element_type_spec: record
            header: clt$type_specification_header,
            qualifier: clt$name_type_qualifier,
          recend,
        recend,
        default_value: string (3),
      recend,
      type5: record
        header: clt$type_specification_header,
      recend,
    recend := [
    [1,
    [88, 12, 7, 15, 53, 25, 196],
    clc$command, 10, 5, 2, 0, 0, 0, 5, ''], [
    ['C                              ',clc$abbreviation_entry, 2],
    ['CATALOG                        ',clc$nominal_entry, 2],
    ['P                              ',clc$abbreviation_entry, 4],
    ['PACKING_LIST                   ',clc$nominal_entry, 1],
    ['PL                             ',clc$abbreviation_entry, 1],
    ['PRODUCT                        ',clc$nominal_entry, 4],
    ['PRODUCTS                       ',clc$alias_entry, 4],
    ['SQC                            ',clc$abbreviation_entry, 3],
    ['STATUS                         ',clc$nominal_entry, 5],
    ['SUBPRODUCT_QUALIFIER_CATALOGS  ',clc$nominal_entry, 3]],
    [
{ PARAMETER 1
    [4, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation, clc$standard_parameter_checking, 3, clc$required_parameter, 0
  , 0],
{ PARAMETER 2
    [2, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation, clc$standard_parameter_checking, 3, clc$required_parameter, 0
  , 0],
{ PARAMETER 3
    [10, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation, clc$standard_parameter_checking, 122,
  clc$optional_default_parameter, 0, 14],
{ PARAMETER 4
    [6, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation, clc$standard_parameter_checking, 85,
  clc$optional_default_parameter, 0, 3],
{ PARAMETER 5
    [9, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name],
    clc$pass_by_reference, clc$immediate_evaluation, clc$standard_parameter_checking, 3,
  clc$optional_parameter, 0, 0]],
{ PARAMETER 1
    [[1, 0, clc$file_type]],
{ PARAMETER 2
    [[1, 0, clc$file_type]],
{ PARAMETER 3
    [[1, 0, clc$union_type], [[clc$keyword_type, clc$list_type],
    FALSE, 2],
    81, [[1, 0, clc$keyword_type], [2], [
      ['LEVEL_AND_TYPE                 ', clc$nominal_entry, clc$normal_usage_entry, 2],
      ['NONE                           ', clc$nominal_entry, clc$normal_usage_entry, 1]]
      ],
    21, [[1, 0, clc$list_type], [5, 1, clc$max_list_size, FALSE],
        [[1, 0, clc$name_type], [1, osc$max_name_size]]
      ]
    ,
    'level_and_type'],
{ PARAMETER 4
    [[1, 0, clc$union_type], [[clc$keyword_type, clc$list_type],
    FALSE, 2],
    44, [[1, 0, clc$keyword_type], [1], [
      ['ALL                            ', clc$nominal_entry, clc$normal_usage_entry, 1]]
      ],
    21, [[1, 0, clc$list_type], [5, 1, clc$max_list_size, FALSE],
        [[1, 0, clc$name_type], [1, osc$max_name_size]]
      ]
    ,
    'all'],
{ PARAMETER 5
    [[1, 0, clc$status_type]]];

?? FMT (FORMAT := ON) ??
?? POP ??

    CONST
      p$packing_list = 1,
      p$catalog = 2,
      p$subproduct_qualifier_catalogs = 3,
      p$product = 4,
      p$status = 5;

    VAR
      pvt: array [1 .. 5] of clt$parameter_value;


    VAR
      called_from_package_software: boolean,
      calling_command: rat$installation_commands,
      file_opened: boolean,
      i: integer,
      ignore_command_compatible_type: rat$subproduct_type,
      ignore_save_previous_cycles: boolean,
      ignore_status: ost$status,
      installation_tasks: rat$task_selections,
      job_status_record: rat$job_status_record,
      local_status: ost$status,
      multiple_job_processing: boolean,
      number_of_steps: rat$step_count,
      packing_list_fid: amt$file_identifier,
      packing_list_file_name: ost$name,
      pacs_catalog_path: rat$path,
      reading_control_record: rat$installation_control_record,
      reading_control_segment_ptr: amt$segment_pointer,
      scratch_segment_pointer: amt$segment_pointer,
      sifs_failed_writing: boolean,
      subproducts_failed_loading: boolean;


?? 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 scratch
{   segment and reading control sequence, and close the
{   packing list file when an abort condition arises.
{
{ NOTES:
{

    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 reading_control_segment_ptr.sequence_pointer <> NIL THEN
        mmp$delete_scratch_segment (reading_control_segment_ptr, ignore_status);
        reading_control_segment_ptr.sequence_pointer := NIL;
      IFEND;

      IF scratch_segment_pointer.sequence_pointer <> NIL THEN
        mmp$delete_scratch_segment (scratch_segment_pointer, ignore_status);
        scratch_segment_pointer.sequence_pointer := NIL;
      IFEND;

      fsp$close_file (packing_list_fid, ignore_status);

    PROCEND abort_handler;

?? OLDTITLE, EJECT ??


    status.normal := TRUE;

    clp$evaluate_parameters (parameter_list, #SEQ (pdt), NIL, ^pvt, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    { The reading control record must be initialized here for use in the
    { main block.

    { The following commented statement will replace the one following it
    { as soon as the appropriate constant is added to the installation
    { command type declaration.
    { calling_command := rac$read_tailored_file;

    calling_command := rac$install_product;
    ignore_command_compatible_type := rac$release;
    ignore_save_previous_cycles := FALSE;

    job_status_record.job_identifier := rac$control_job_identifier;
    job_status_record.number_of_steps := 1;
    job_status_record.step_number := 0;

    reading_control_record.job_identifier := job_status_record.job_identifier;
    reading_control_record.job_status_record_p := ^job_status_record;
    reading_control_segment_ptr.kind := amc$sequence_pointer;
    reading_control_segment_ptr.sequence_pointer := NIL;

    scratch_segment_pointer.kind := amc$sequence_pointer;
    scratch_segment_pointer.sequence_pointer := NIL;

    sifs_failed_writing := FALSE;
    subproducts_failed_loading := FALSE;
    file_opened := FALSE;
    called_from_package_software := TRUE;

    { This module does not do a true product installation, but rather
    { a loading of subproducts into a catalog structure from which
    { customer orders can be generated.  Therefore, the storage
    { class must be ignored.

    rav$installation_defaults.ignore_storage_class := TRUE;

    osp$establish_block_exit_hndlr (^abort_handler);

  /main/
    BEGIN

      mmp$create_scratch_segment (amc$sequence_pointer, mmc$as_random, scratch_segment_pointer, status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;
      reading_control_record.scratch_seq_p := scratch_segment_pointer.sequence_pointer;

      RESET reading_control_record.scratch_seq_p;

      separate_catalog_from_file_name (pvt [p$packing_list].value^.file_value,
            rav$installation_defaults.installation_database, packing_list_file_name, status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;

      rap$init_processing_seq (packing_list_file_name, ignore_save_previous_cycles, calling_command,
            ignore_command_compatible_type, rav$installation_defaults, reading_control_record,
            reading_control_segment_ptr, packing_list_fid, file_opened, status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;

      initialize_for_product_load (pvt [p$catalog].value^.file_value,
            pvt [p$subproduct_qualifier_catalogs].value, pvt [p$product].value, reading_control_record,
            status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;

      rap$load_products (called_from_package_software, reading_control_record,
            subproducts_failed_loading, status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;

      IF subproducts_failed_loading THEN
        osp$set_status_abnormal ('RA', rae$reatf_load_failure, '', local_status);
        osp$generate_error_message (local_status, ignore_status);
      IFEND;

      recreate_subproduct_sifs (reading_control_record.subproduct_processing_records_p, sifs_failed_writing,
            status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;

    END /main/;

    IF status.normal AND (subproducts_failed_loading OR sifs_failed_writing) THEN
      osp$set_status_abnormal ('RA', rae$pacs_catalogs_not_restored, '', status);
    IFEND;

    IF file_opened THEN
      fsp$close_file (packing_list_fid, local_status);
      IF status.normal AND (NOT local_status.normal) THEN
        status := local_status;
      IFEND;
    IFEND;

    IF reading_control_segment_ptr.sequence_pointer <> NIL THEN
      mmp$delete_scratch_segment (reading_control_segment_ptr, local_status);
      reading_control_segment_ptr.sequence_pointer := NIL;
      IF status.normal AND (NOT local_status.normal) THEN
        status := local_status;
      IFEND;
    IFEND;

    IF scratch_segment_pointer.sequence_pointer <> NIL THEN
      mmp$delete_scratch_segment (scratch_segment_pointer, local_status);
      scratch_segment_pointer.sequence_pointer := NIL;
      IF status.normal AND (NOT local_status.normal) THEN
        status := local_status;
      IFEND;
    IFEND;

    osp$disestablish_cond_handler;

  PROCEND rap$read_tailored_file;

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

{ PURPOSE:
{   This procedure assembles the PACS catalog path for the subproduct.
{
{ DESIGN:
{   The PACS catalog path is assembled by taking the CATALOG_P path,
{   appending the subproduct's name and one of the following three cases
{   based on the subproduct qualifier catalog specified.  If the subproduct
{   qualifier catalog is specified as level and type, the subproduct's level
{   and type (from the SIF) are appended to the PACS catalog path.  If
{   subproduct qualifier catalog is a list of names, these names are
{   appended to the PACS catalog path.  When the subproduct qualifier
{   catalog is specified as none, nothing is added to the PACS catalog path.
{
{   The subproduct's PACS catalog is stored in the processing sequence.  The
{   catalog is verified to not exist.
{
{ NOTES:
{

  PROCEDURE assemble_pacs_catalog
    (    catalog_p: ^fst$file_reference;
         subproduct_qualifier_catalogs: rat#subprod_qualifier_catalogs;
         subproduct_name: rat$subproduct_name;
         subproduct_level: rat$subproduct_level;
         subproduct_type: rat$subproduct_type;
     VAR pacs_catalog_p: ^pft$path;
     VAR processing_seq_p: ^rat$processing_sequence;
     VAR scratch_seq_p: ^SEQ ( * );
     VAR subproduct_validation_error: boolean;
     VAR status: ost$status);


    VAR
      catalog_exists: boolean,
      ignore_status: ost$status,
      local_status: ost$status,
      pacs_catalog: rat$path;


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

    { Assign the PACS catalog path for the subproduct.

    IF subproduct_qualifier_catalogs.kind = rac#level_and_type THEN

      { Concatenate the catalog with the subproduct name, level and type
      { (in that order) to form the PACS catalog path for the subproduct
      { selected for loading.

      STRINGREP (pacs_catalog.path, pacs_catalog.size, catalog_p^, '.',
            subproduct_name (1, clp$trimmed_string_size (subproduct_name)),
            '.', subproduct_level (1, clp$trimmed_string_size (subproduct_level)),
            '.', rav$subproduct_type [subproduct_type] (1, clp$trimmed_string_size
            (rav$subproduct_type [subproduct_type])));

    ELSEIF subproduct_qualifier_catalogs.kind = rac#none THEN

      { Concatenate the catalog with the subproduct name to form the
      { PACS catalog path for the subproduct selected for loading.

      STRINGREP (pacs_catalog.path, pacs_catalog.size, catalog_p^, '.',
            subproduct_name (1, clp$trimmed_string_size (subproduct_name)));

    ELSE { subproduct_qualifier_catalogs.kind = rac#user_specified }

      { Concatenate the catalog with the subproduct name and the user
      { specified qualifier_catalogs to form the PACS catalog path for the
      { subproduct selected for loading.

      STRINGREP (pacs_catalog.path, pacs_catalog.size, catalog_p^, '.',
            subproduct_name (1, clp$trimmed_string_size (subproduct_name)),
            subproduct_qualifier_catalogs.user_specified.path
            (1, subproduct_qualifier_catalogs.user_specified.size));

    IFEND;

    { Convert the PACS catalog path from a string to PF format and
    { store in the installation catalog path in the reading control
    { record.  This store is made to accomodate subsequent processing
    { which requires this value.

    rap$convert_path_to_pf_format (pacs_catalog, pacs_catalog_p, processing_seq_p, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    { At this point, a check is made to determine if the PACS catalog
    { exists.  If it does, an error message is logged, and a RETURN
    { issued to terminate processing.  The PACS catalog cannot exist prior
    { to the initiation of the reading of a product tape.  See the notes above
    { for more details.

    rap$verify_catalog_exists (pacs_catalog_p^, scratch_seq_p, catalog_exists);
    IF catalog_exists THEN
      osp$set_status_abnormal ('RA', rae$pacs_catalog_already_exists, '', local_status);
      osp$append_status_file (osc$status_parameter_delimiter, pacs_catalog.path (1, pacs_catalog.size),
            local_status);
      osp$append_status_parameter (osc$status_parameter_delimiter,
            subproduct_name (1, clp$trimmed_string_size (subproduct_name)), local_status);
      osp$generate_error_message (local_status, ignore_status);
      subproduct_validation_error := TRUE;
      RETURN;
    IFEND;

  PROCEND assemble_pacs_catalog;

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

{ PURPOSE:
{   This procedure reformats the SUBPRODUCT_QUALIFIER_CATALOG parameter
{   input for processing.
{
{ DESIGN:
{   When the qualifier catalog input is a keyword the related module level
{   constant is assigned.  When the qualifier catalog input is a list of
{   names, the names are assembled into a path.  Each name represents a
{   subcatalog.
{
{ NOTES:
{

  PROCEDURE format_qualifier_catalogs_input
    (    qualifier_catalogs_input_p: ^clt$data_value;
     VAR subproduct_qualifier_catalogs: rat#subprod_qualifier_catalogs);


    VAR
      current_p: ^clt$data_value,
      length: integer,
      next_position: integer;


    IF qualifier_catalogs_input_p^.kind = clc$keyword THEN

      IF qualifier_catalogs_input_p^.keyword_value = 'NONE' THEN
        subproduct_qualifier_catalogs.kind := rac#none;
      ELSE { qualifier_catalogs_input_p^.keyword_value = 'LEVEL_AND_TYPE' }
        subproduct_qualifier_catalogs.kind := rac#level_and_type;
      IFEND;

      subproduct_qualifier_catalogs.user_specified.size := 0;

    ELSE {list of names specified}

      subproduct_qualifier_catalogs.kind := rac#user_specified;

      next_position := 1;
      current_p := qualifier_catalogs_input_p;
      WHILE current_p <> NIL DO

        length := clp$trimmed_string_size (current_p^.element_value^.name_value);

        subproduct_qualifier_catalogs.user_specified.path (next_position, * ) := '.';
        next_position := next_position + 1;
        subproduct_qualifier_catalogs.user_specified.path (next_position, * ) :=
              current_p^.element_value^.name_value (1, length);
        next_position := next_position + length;

        current_p := current_p^.link;
      WHILEND;

      subproduct_qualifier_catalogs.user_specified.size := next_position - 1;

    IFEND;

  PROCEND format_qualifier_catalogs_input;

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

{ PURPOSE:
{   This procedure performs all necessary initialization steps
{   prior to reading the subproducts into the appropriate catalog
{   structure.
{
{ DESIGN:
{   This procedure performs all the required initialization steps prior
{   to reading the product tape.
{
{   The job processing record is established.  Only a single job is used to
{   read in the tailored tape; therefore, only one record is defined, and
{   the appropriate job identifier is defined.
{
{   The subproduct qualifier catalogs parameter is reformated for subsequent
{  processing.
{
{   The subproduct processing records are established.  One record is
{   defined for each subproduct referenced by the product list.
{
{ NOTES:
{

  PROCEDURE initialize_for_product_load
    (    catalog_p: ^fst$file_reference;
         qualifier_catalogs_input_p: ^clt$data_value;
         product_list_p: ^clt$data_value;
     VAR reading_control_record {input, output} : rat$installation_control_record;
     VAR status: ost$status);


    VAR
      subproduct_qualifier_catalogs: rat#subprod_qualifier_catalogs;


    status.normal := TRUE;

    { Establish the step set and number of steps values.  The only step being performed
    { is to load subproducts.

    reading_control_record.processing_header_p^.step_set := $rat$step_selections [rac$load_subproducts_step];
    reading_control_record.processing_header_p^.number_of_steps := 1;

    { Establish the job processing record.  Only one job is used to read the tape.

    NEXT reading_control_record.job_processing_records_p: [1 .. 1] IN reading_control_record.processing_seq_p;
    IF reading_control_record.job_processing_records_p = NIL THEN
      osp$set_status_abnormal ('RA', rae$accessed_beyond_segment_eoi, 'PROCESSING SEQUENCE', status);
      osp$append_status_parameter (osc$status_parameter_delimiter, 'JOB PROCESSING RECORDS', status);
      RETURN;
    IFEND;

    reading_control_record.processing_header_p^.job_processing_rec_rel_p :=
          #REL (reading_control_record.job_processing_records_p, reading_control_record.processing_seq_p^);

    reading_control_record.job_processing_records_p^ [1].job_identifier :=
          reading_control_record.job_identifier;


    format_qualifier_catalogs_input (qualifier_catalogs_input_p, subproduct_qualifier_catalogs);

    process_product_list (product_list_p, catalog_p, subproduct_qualifier_catalogs, reading_control_record,
          status);

  PROCEND initialize_for_product_load;

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

{ PURPOSE:
{   This procedure registers the subproduct as selected for reading.  Any
{   validation errors encountered are flagged.
{
{ DESIGN:
{   This procedure assembles the subproduct's PACS path and sets the
{   required task and job information to get that subproduct read.
{
{   The subproduct count for the appropriate medium processing record is
{   incremented.  If the medium is tape the subproduct count for the primary
{   tape that the subproduct resides is incremented.  For disk orders there
{   is only 1 medium processing record.
{
{ NOTES:
{

  PROCEDURE initialize_for_subproduct_load
    (    packing_list_pointers: rat$packing_list_pointers;
         subproduct_index: rat$subproduct_count;
         catalog_p: ^fst$file_reference;
         subproduct_qualifier_catalogs: rat#subprod_qualifier_catalogs;
         job_identifier: rat$job_identifier;
         medium_processing_records_p { input, output } : ^rat$medium_processing_records;
     VAR subproduct_processing_record { input, output } : rat$subp_processing_record;
     VAR processing_seq_p: ^rat$processing_sequence;
     VAR scratch_seq_p: ^SEQ ( * );
     VAR subproduct_validation_error: boolean;
     VAR status: ost$status);


    VAR
      medium_index: rat$tape_count;


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

    { Assign the PACS catalog path for the subproduct.

    assemble_pacs_catalog (catalog_p, subproduct_qualifier_catalogs,
          subproduct_processing_record.subproduct_info_pointers.attributes_p^.name,
          subproduct_processing_record.subproduct_info_pointers.attributes_p^.level,
          subproduct_processing_record.subproduct_info_pointers.attributes_p^.subproduct_type,
          subproduct_processing_record.installation_catalog_p, processing_seq_p, scratch_seq_p,
          subproduct_validation_error, status);
    IF (NOT status.normal) OR (subproduct_validation_error) THEN
      RETURN;
    IFEND;

    { Assign the installation task set and user reference (product type) to the subproduct.

    subproduct_processing_record.task_set := $rat$task_selections [rac$load_files_task];
    subproduct_processing_record.task_status := rac$task_started;

    { Increment the appropriate medium processing record's subproduct count and
    { set the job identifier.

    IF packing_list_pointers.order_medium = rac$tape THEN
      medium_index := packing_list_pointers.tape_subproduct_indexer_p^ [subproduct_index].primary_tape_vsn;
    ELSE { order medium = rac$disk }
      medium_index := 1;
    IFEND;

    medium_processing_records_p^ [medium_index].subproduct_count :=
          medium_processing_records_p^ [medium_index].subproduct_count + 1;
    medium_processing_records_p^ [medium_index].job_identifier := job_identifier;

    { Assign the job responsible to load the subproduct.

    subproduct_processing_record.job_identifier := job_identifier;

  PROCEND initialize_for_subproduct_load;

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

{ PURPOSE:
{   This procedure initializes the subproduct processing records based on
{   the product list.
{
{ DESIGN:
{   The appropriate product list processing procedure is called (key ALL or
{   names).  When the product list is a list of names the product list is
{   checked for keyword ALL.  An error is returned if names are specified
{   with keyword ALL.
{
{ NOTES:
{   ** We need to test that we won't be trying to load into the same PACS
{   catalog.  This can occur when a tailored file contains both a release
{   and a correction for the same subproduct and the subproduct qualifier
{   catalogs is not LEVEL_AND_TYPE.  If this condition is detected we should
{   flag it as a validation error.
{

  PROCEDURE process_product_list
    (    product_list_p: ^clt$data_value;
         catalog_p: ^fst$file_reference;
         subproduct_qualifier_catalogs: rat#subprod_qualifier_catalogs;
     VAR reading_control_record {input, output} : rat$installation_control_record;
     VAR status: ost$status);


    VAR
      current_p: ^clt$data_value;


    status.normal := TRUE;

    { Process the product list based on whether its the keyword ALL or a list of names.

    IF product_list_p^.kind = clc$keyword THEN

      process_product_list_all (catalog_p, subproduct_qualifier_catalogs, reading_control_record, status);

    ELSE {list of names specified}

      { Test that key ALL is not specified along with product names.

      current_p := product_list_p;
      WHILE current_p <> NIL DO
        IF current_p^.element_value^.name_value = 'ALL' THEN
          osp$set_status_abnormal ('RA', rae$specified_names_and_key_all, '', status);
          RETURN;
        IFEND;
        current_p := current_p^.link;
      WHILEND;

      process_product_list_names (product_list_p, catalog_p, subproduct_qualifier_catalogs,
            reading_control_record, status);

    IFEND;

  PROCEND process_product_list;

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

{ PURPOSE:
{   This procedure processes the product list as the keyword ALL.
{
{ DESIGN:
{   All subproducts known to the packing list have been selected for reading.
{
{   A validation error does not stop subproduct validation from continuing
{   but it will terminate the rest of the reading process.  This allows
{   all the validation errors to be discovered at one time.
{
{ NOTES:
{

  PROCEDURE process_product_list_all
    (    catalog_p: ^fst$file_reference;
         subproduct_qualifier_catalogs: rat#subprod_qualifier_catalogs;
     VAR reading_control_record {input, output} : rat$installation_control_record;
     VAR status: ost$status);


    VAR
      subproduct_index: rat$subproduct_count,
      subproduct_validation_error: boolean,
      validation_errors_occurred: boolean;


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

  /main/
    FOR subproduct_index := 1 TO UPPERBOUND (reading_control_record.subproduct_processing_records_p^) DO

      subproduct_validation_error := FALSE;
      initialize_for_subproduct_load (reading_control_record.packing_list_pointers, subproduct_index,
            catalog_p, subproduct_qualifier_catalogs, reading_control_record.job_identifier,
            reading_control_record.medium_processing_records_p,
            reading_control_record.subproduct_processing_records_p^ [subproduct_index],
            reading_control_record.processing_seq_p, reading_control_record.scratch_seq_p,
            subproduct_validation_error, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

      validation_errors_occurred := (validation_errors_occurred OR subproduct_validation_error);
    FOREND /main/;

    IF validation_errors_occurred THEN
      osp$set_status_abnormal ('RA', rae$reatf_validation_errors, 'PRODUCT', status);
    IFEND;

  PROCEND process_product_list_all;

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

{ PURPOSE:
{   This procedure processes the product list as a list of names.
{
{ DESIGN:
{   Each name from the product list is examined to determine which
{   subproducts should be selected for the installation event.  A product
{   name can be a licensed product, subproduct or group name, which
{   references a set of one or more subproducts.  A validation error is
{   returned when the product name does not.
{
{   A validation error does not stop product list validation from continuing
{   but it will terminate the rest of the installation processing.  This
{   allows all the validation errors to be discovered at one time.
{
{ NOTES:
{

  PROCEDURE process_product_list_names
    (    product_list_p: ^clt$data_value;
         catalog_p: ^fst$file_reference;
         subproduct_qualifier_catalogs: rat#subprod_qualifier_catalogs;
     VAR reading_control_record {input, output} : rat$installation_control_record;
     VAR status: ost$status);


    VAR
      current_product_list_p: ^clt$data_value,
      group_name: ost$name,
      ignore_status: ost$status,
      j: 0 .. rac$max_additional_products,
      local_status: ost$status,
      product_name: ost$name,
      product_reference: boolean,
      subproduct_attributes_p: ^rat$subproduct_attributes,
      subproduct_index: rat$subproduct_count,
      subproduct_validation_error: boolean,
      validation_errors_occurred: boolean;


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

    current_product_list_p := product_list_p;
    WHILE current_product_list_p <> NIL DO
      product_name := current_product_list_p^.element_value^.name_value;
      product_reference := FALSE;

    /main/
      FOR subproduct_index := 1 TO UPPERBOUND (reading_control_record.subproduct_processing_records_p^) DO
        subproduct_attributes_p := reading_control_record.subproduct_processing_records_p^ [subproduct_index].
              subproduct_info_pointers.attributes_p;

        subproduct_validation_error := FALSE;

        IF (product_name = subproduct_attributes_p^.licensed_product) OR
              (product_name = subproduct_attributes_p^.name) THEN
          product_reference := TRUE;
          initialize_for_subproduct_load (reading_control_record.packing_list_pointers, subproduct_index,
                catalog_p, subproduct_qualifier_catalogs, reading_control_record.job_identifier,
                reading_control_record.medium_processing_records_p,
                reading_control_record.subproduct_processing_records_p^ [subproduct_index],
                reading_control_record.processing_seq_p, reading_control_record.scratch_seq_p,
                subproduct_validation_error, status);

        ELSE {check for group name}
          group_name (1, * ) := rac$group_designator;
          group_name (clp$trimmed_string_size (rac$group_designator) + 1, * ) :=
                product_name (1, clp$trimmed_string_size (product_name));

        /group_check/
          FOR j := 1 TO UPPERBOUND (subproduct_attributes_p^.additional_products) DO
            IF group_name = subproduct_attributes_p^.additional_products [j] THEN
              product_reference := TRUE;
              initialize_for_subproduct_load (reading_control_record.packing_list_pointers, subproduct_index,
                    catalog_p, subproduct_qualifier_catalogs, reading_control_record.job_identifier,
                    reading_control_record.medium_processing_records_p,
                    reading_control_record.subproduct_processing_records_p^ [subproduct_index],
                    reading_control_record.processing_seq_p, reading_control_record.scratch_seq_p,
                    subproduct_validation_error, status);

              EXIT /group_check/;
            IFEND;
          FOREND /group_check/;

        IFEND;

        IF NOT status.normal THEN
          RETURN;
        IFEND;

        validation_errors_occurred := (validation_errors_occurred OR subproduct_validation_error);
      FOREND /main/;

      IF NOT product_reference THEN
        osp$set_status_abnormal ('RA', rae$unknown_product_name,
              product_name (1, clp$trimmed_string_size (product_name)), local_status);
        osp$generate_error_message (local_status, ignore_status);
        validation_errors_occurred := TRUE;
      IFEND;

      current_product_list_p := current_product_list_p^.link;
    WHILEND;

    IF validation_errors_occurred THEN
      osp$set_status_abnormal ('RA', rae$reatf_validation_errors, 'PRODUCT', status);
      RETURN;
    IFEND;

  PROCEND process_product_list_names;

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

{ PURPOSE:
{   This procedure re-creates the individual subproduct information
{   files (SIF) for each subproduct represented in reading_control_record.
{
{ DESIGN:
{   Each subproduct identifier in the packing list (ie. listed in the
{   reading control record) is processed.
{
{   The path to the PACS catalog is converted to string format
{   which is required in subsequent steps.  The name of the SIF file
{   is appended to the PACS catalog name.  The SIF data is subsequently
{   written from memory to the SIF permanent file.
{
{   The subproduct is verified via the RAP$VERIFY_SUBPRODUCT_INTERFACE
{   routine.  The verify_option RECONCILE_EFFECTS_OF_RESTORE is
{   specified to modify the SIF to reflect the new catalog structure
{   is now exists in.
{
{ NOTES:
{


  PROCEDURE recreate_subproduct_sifs
    (    subproduct_processing_records_p: ^rat$subp_processing_records;
     VAR processing_errors_occurred: boolean;
     VAR status: ost$status);


    VAR
      i: integer,
      ignore_status: ost$status,
      local_status: ost$status,
      pacs_catalog: rat$path,
      reconcile_effects_of_restore: [STATIC, READ] ost$name := 'reconcile_effects_of_restore',
      sif_identifier: ost$name,
      subproduct_info_file: rat$path,
      write_status: ost$status;


    status.normal := TRUE;
    processing_errors_occurred := FALSE;
    sif_identifier := '';

  /main/
    FOR i := 1 TO UPPERBOUND (subproduct_processing_records_p^) DO

      IF (rac$load_files_task IN subproduct_processing_records_p^ [i].task_set) AND
            (subproduct_processing_records_p^ [i].task_status <> rac$task_failed) THEN

        rap$convert_path_to_str (subproduct_processing_records_p^ [i].installation_catalog_p^, pacs_catalog);

        STRINGREP (subproduct_info_file.path, subproduct_info_file.size, pacs_catalog.
              path (1, pacs_catalog.size), '.', rac$sif_file_name);

        rap$write_file_from_memory (subproduct_info_file.path (1, subproduct_info_file.size),
              #SIZE (subproduct_processing_records_p^ [i].subproduct_info_pointers.subproduct_info_seq_p^),
              subproduct_processing_records_p^ [i].subproduct_info_pointers.subproduct_info_seq_p,
              write_status);
        IF NOT write_status.normal THEN
          osp$set_status_abnormal ('RA', rae$sif_writing_error, '', local_status);
          osp$append_status_file (osc$status_parameter_delimiter, subproduct_info_file.
                path (1, subproduct_info_file.size), local_status);
          osp$generate_error_message (local_status, ignore_status);
          osp$generate_error_message (write_status, ignore_status);
          processing_errors_occurred := TRUE;
          CYCLE /main/;
        IFEND;

        rap$verify_subproduct_interface (^pacs_catalog.path (1, pacs_catalog.size),
              reconcile_effects_of_restore, sif_identifier, local_status);
        IF NOT local_status.normal THEN
          osp$generate_error_message (local_status, ignore_status);
          processing_errors_occurred := TRUE;
        IFEND;

      IFEND;
    FOREND /main/;

  PROCEND recreate_subproduct_sifs;

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

{ PURPOSE:
{   This procedure separates the catalog portion of a path from the file
{   portion, and places each separate part in parameters whose values are
{   returned to the calling procedure.
{
{ DESIGN:
{   Locate the last period in the file reference parameter variable.  The
{   characters to the left of this last period represent the path to the
{   file.  The characters to the right of the this last period represent
{   the file name in the catalog.
{
{ NOTES:
{

  PROCEDURE separate_catalog_from_file_name
    (    file_reference_p: ^fst$file_reference;
     VAR catalog_path: rat$path;
     VAR file_name: ost$name;
     VAR status: ost$status);


    VAR
      fs_path: string (fsc$max_path_size),
      ignore_cycle_reference: fst$cycle_reference,
      ignore_open_position: fst$open_position,
      number_of_elements: fst$number_of_path_elements,
      position: integer;


    status.normal := TRUE;

    { The value of position will be the character position of the period separating the file's catalog
    { path from the file name (ie. the last period in the file reference) after the WHILE loop completes.

    pfp$convert_string_to_fs_path (file_reference_p^ (1, STRLENGTH (file_reference_p^)), fs_path,
          number_of_elements, ignore_cycle_reference, ignore_open_position, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    position := clp$trimmed_string_size (fs_path);

{   position := STRLENGTH (file_reference_p^);
    WHILE file_reference_p^ (position) <> '.' DO
      position := position - 1;
      IF position <= 0 THEN
        { Set bad status. }
        RETURN;
      IFEND;
    WHILEND;

    { Retreive the file's catalog path.

    catalog_path.path (1, * ) := file_reference_p^ (1, position - 1);
    catalog_path.size := position - 1;

    { Retreive the file's name.

    file_name (1, * ) := file_reference_p^ (position + 1, (STRLENGTH (file_reference_p^) - position));

  PROCEND separate_catalog_from_file_name;

MODEND ram$read_tailored_file;
