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

{ PURPOSE:
{   This module contains the interface and procedures that validate for
{   installation.
{
{ DESIGN:
{   The compiled module resides in RAF$LIBRARY.
{
{ NOTES:
{

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc rae$install_software_cc
*copyc rac$control_job_identifier
*copyc rac$idb_directory_level
*copyc rac$idb_directory_name
*copyc rac$inss_processor_version
*copyc rac$not_installed
*copyc rac$packing_list_level
*copyc rac$pacs_processor_version
*copyc rac$special_product_designators
*copyc rac$undefined_inst_path_element
*copyc clt$data_value
*copyc fst$path
*copyc rat$idb_directory_pointers
*copyc rat$installation_control_record
*copyc rat$packing_list_sequence
*copyc rat$product_references
*copyc rat$sequence_descriptor_types
*copyc rat$subproduct_install_paths
?? POP ??
*copyc amp$get_file_attributes
*copyc amp$get_segment_pointer
*copyc amp$set_segment_eoi
*copyc clp$trimmed_string_size
*copyc fsp$close_file
*copyc fsp$open_file
*copyc ofp$receive_operator_response
*copyc ofp$send_operator_message
*copyc osp$append_status_file
*copyc osp$append_status_parameter
*copyc osp$establish_block_exit_hndlr
*copyc osp$disestablish_cond_handler
*copyc osp$generate_message
*copyc osp$generate_log_message
*copyc osp$set_status_abnormal
*copyc pfp$change
*copyc pfp$convert_fs_path_to_pf_path
*copyc pfp$convert_string_to_fs_path
*copyc pmp$get_compact_date_time
*copyc rap$access_directory_for_read
*copyc rap$access_directory_for_write
*copyc rap$assemble_installation_path
*copyc rap$establish_directory_ptrs
*copyc rap$locate_directory_record
*copyc rap$validate_for_correction
*copyc rav$product_reference
*copyc rav$subproduct_type
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

  TYPE
    rat#licensed_product_references = record
      list_length: rat$subproduct_count,
      list_p: ^array [ * ] of rat#reference_record,
    recend;

  TYPE
    rat#reference_record = record
      name: ost$name,
      selected: boolean,
    recend;

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

{ PURPOSE:
{   This interface validates the installation request.
{
{ DESIGN:
{   Everything that can possibly be checked, that can abort the
{   installation, is checked at this time.  This is to eliminate or reduce
{   the potential for encountering an error after the installation work
{   begins.  Once installation processing begins, any errors encountered
{   involves backing out of any work that was already completed.
{
{   At 1.4.1 validation consists of validating the input list and the
{   ability to update the IDB Directory.  Also correction base validation is
{   performed (when dealing with corrections).  In the future the validating
{   may include other items, such as:
{
{     a.  Validate that the user has the authority to install into the
{         subproducts installation paths.
{
{     b.  Minimum ring is compared with file ring values (is the user
{         able to task down and create the lowest ring defined for the
{         files to be installed.)  This will not be needed if the user is
{         the site administrator or running from the system console.
{
{     c.  The amount of space required for the installation will be
{         calculated and the space will be verified as being available.
{         (At 1.4.1 the user is responsible for managing the disk space
{         needs.  The display_packing_list command will provide the user
{         with the subproduct size requirements.)
{
{ NOTES:
{

  PROCEDURE [XDCL] rap$validate_for_installation
    (    product_list_p: ^clt$data_value;
         excluded_product_list_p: ^clt$data_value;
         force_reinstall: boolean;
         installation_tasks: rat$task_selections;
     VAR installation_control_record {input, output} : rat$installation_control_record;
     VAR status: ost$status);


    VAR
      directory_fid: amt$file_identifier,
      directory_file_opened: boolean,
      directory_pointers: rat$idb_directory_pointers,
      ignore_status: ost$status,
      local_status: ost$status;


?? 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 close the IDB directory
{   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;

      fsp$close_file (directory_fid, ignore_status);

    PROCEND abort_handler;

?? OLDTITLE, EJECT ??


    status.normal := TRUE;

    osp$establish_block_exit_hndlr (^abort_handler);

  /main/
    BEGIN

      access_directory_for_validation (installation_control_record.processing_header_p^.
            installation_defaults.installation_database, directory_pointers, directory_fid,
            directory_file_opened, status);
      IF (NOT status.normal) AND (status.condition = rae$incompatible_sequence_level) THEN
        { Replace incompatible directory with a new directory.

        replace_incompatible_directory (installation_control_record.processing_header_p^.
            installation_defaults.installation_database, directory_pointers, directory_fid,
            directory_file_opened, status);

      IFEND;
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;

      { Validate the product list.

      validate_product_list (product_list_p, excluded_product_list_p, force_reinstall, directory_pointers,
            installation_tasks, installation_control_record, status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;

      IF installation_control_record.processing_header_p^.command_compatible_type = rac$correction THEN
        rap$validate_for_correction (directory_pointers, installation_control_record, status);
      IFEND;

    END /main/;

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

    osp$disestablish_cond_handler;

  PROCEND rap$validate_for_installation;

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

{ PURPOSE:
{   This procedure access the directory for validation.
{
{ DESIGN:
{
{ NOTES:
{

  PROCEDURE access_directory_for_validation
    (    installation_database: rat$path;
     VAR directory_pointers: rat$idb_directory_pointers;
     VAR directory_fid: amt$file_identifier;
     VAR directory_file_opened: boolean;
     VAR status: ost$status);


    VAR
      ignore_directory_segment_ptr: amt$segment_pointer;


    status.normal := TRUE;

    { Validate that the IDB directory can be accessed for write mode.  At the same time assuring that
    { the directory is initialized.  Immediately close the directory and re-access it in read mode.

    rap$access_directory_for_write (installation_database, ignore_directory_segment_ptr, directory_fid,
          directory_file_opened, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    IF directory_file_opened THEN
      fsp$close_file (directory_fid, status);
      directory_file_opened := FALSE;
      IF NOT status.normal THEN
        RETURN;
      IFEND;
    IFEND;

    rap$access_directory_for_read (installation_database, directory_pointers, directory_fid,
          directory_file_opened, status);

  PROCEND access_directory_for_validation;

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

{ PURPOSE:
{   This procedure processes the licensed product references made by a
{   specified subproduct or group name from the product list and any
{   references from the excluded product list.
{
{ DESIGN:
{   When a subproduct or group from the product list indirectly references a
{   licensed product that was not directly specified, the IDB directory must
{   be updated to record the licensed product reference.  This is also true
{   for any licensed product references by the excluded product list.
{
{   The licensed product references list is first checked to see if the
{   licensed product name is already on the list.  The licensed product name
{   is processed according to how it was referenced as follows:
{
{     1. When the product reference is from the excluded product list
{        add name to list if not found.  Set the select field
{        to not selected.  If name already on list do nothing.
{
{     2. When the product reference is by group or subproduct add name
{        to list if not found.  In either case set the select field to
{        selected.
{
{     3. When the product referece is by licensed product set the field
{        selected if name is already on the list.  Do nothing if name is
{        not on the list.
{
{ NOTES:
{   The subproduct processing records array contains a record for each
{   subproduct available for processing.  There cannot be more licensed
{   products than subproducts.  Since, the length of the list array used
{   here is established to be the length of the subproduct processing
{   records, it follows that the index can never exceed the upperbound
{   of the list array.  Therefore, no test is present.
{

  PROCEDURE add_to_licensed_prod_references
    (    product_reference: rat$product_references;
         licensed_product: ost$name;
     VAR licensed_product_references {input, output} : rat#licensed_product_references);


    VAR
      index: rat$subproduct_count,
      name_found: boolean;


    name_found := FALSE;
    index := 0;
    WHILE (NOT name_found) AND (index < licensed_product_references.list_length) DO
      index := index + 1;
      IF licensed_product_references.list_p^ [index].name = licensed_product THEN
        name_found := TRUE;
      IFEND;
    WHILEND;


    IF name_found THEN
      IF product_reference <> rac$excluded THEN
        licensed_product_references.list_p^ [index].selected := TRUE;
      IFEND;
    ELSE {name not in list}
      IF product_reference <> rac$licensed_product THEN
        licensed_product_references.list_length := licensed_product_references.list_length + 1;
        licensed_product_references.list_p^ [licensed_product_references.list_length].name :=
              licensed_product;
        IF product_reference = rac$excluded THEN
          licensed_product_references.list_p^ [licensed_product_references.list_length].selected := FALSE;
        ELSE {product reference is rac$subproduct or rac$group}
          licensed_product_references.list_p^ [licensed_product_references.list_length].selected := TRUE;
        IFEND;
      IFEND;
    IFEND;

  PROCEND add_to_licensed_prod_references;

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

{ PURPOSE:
{   This procedure displays an informative message to $RESPONSE declaring that
{   the product (a licensed product or group) was rejected for processing
{   because all of it's associated subproducts were found to already be
{   installed.
{
{ DESIGN:
{   This is not a validation error, processing will continue.
{
{ NOTES:
{

  PROCEDURE display_product_rejected_msg
    (    some_subproducts_selected: boolean;
         product_reference: rat$product_references;
         product_name: ost$name);


    VAR
      ignore_status: ost$status,
      local_status: ost$status;


    IF some_subproducts_selected THEN

      osp$set_status_abnormal ('RA', rae$subproducts_rejected, 'Some', local_status);

    ELSE {none were selected}

      osp$set_status_abnormal ('RA', rae$subproducts_rejected, 'All', local_status);

    IFEND;

    osp$append_status_parameter (osc$status_parameter_delimiter, rav$product_reference [product_reference],
          local_status);

    osp$append_status_parameter (osc$status_parameter_delimiter,
          product_name (1, clp$trimmed_string_size (product_name)), local_status);

    osp$generate_message (local_status, ignore_status);

  PROCEND display_product_rejected_msg;

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

{ PURPOSE:
{   This procedure displays one of several informative messages to the job
{   log declaring that the subproduct (belonging to a licensed product or
{   group) was rejected for processing because of the condition that was
{   passed in.
{
{ DESIGN:
{   There are several rejection messages using the same parameters being
{   displayed to the job log.  The commonality of the code was the reason
{   for this procedure.  When the product type is subproduct, the message is
{   constructed using the licensed product name which is also passed in.
{
{   This is not a validation error, processing will continue.
{
{ NOTES:
{

  PROCEDURE display_subproduct_rejected_msg
    (    subproduct_name: rat$subproduct_name;
         licensed_product: rat$licensed_product;
         condition_for_rejection: ost$status_condition_code;
         product_name: ost$name;
         product_reference: rat$product_references);


    VAR
      ignore_status: ost$status,
      local_status: ost$status;


    osp$set_status_abnormal ('RA', condition_for_rejection,
          subproduct_name (1, clp$trimmed_string_size (subproduct_name)), local_status);

    IF product_reference <> rac$subproduct THEN

      osp$append_status_parameter (osc$status_parameter_delimiter, rav$product_reference [product_reference],
            local_status);

      osp$append_status_parameter (osc$status_parameter_delimiter,
            product_name (1, clp$trimmed_string_size (product_name)), local_status);

    ELSE {subproduct was directly referenced by the user}

      osp$append_status_parameter (osc$status_parameter_delimiter,
            rav$product_reference [rac$licensed_product], local_status);

      osp$append_status_parameter (osc$status_parameter_delimiter,
            licensed_product (1, clp$trimmed_string_size (licensed_product)), local_status);

    IFEND;

    osp$generate_log_message ($pmt$ascii_logset [pmc$job_log], local_status, ignore_status);

  PROCEND display_subproduct_rejected_msg;

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

{ PURPOSE:
{   This procedure processes the excluded product list.
{
{ DESIGN:
{   Each name from the excluded product list is examined to determine which
{   subproducts should be excluded from the installation event.  A product
{   name can be a licensed product, subproduct or group name, which
{   references a set of 1 or more subproducts.
{
{ NOTES:
{

  PROCEDURE process_excluded_product_list
    (    excluded_product_list_p: ^clt$data_value;
     VAR licensed_product_references {input, output} : rat#licensed_product_references;
     VAR installation_control_record {input, output} : rat$installation_control_record);


    VAR
      current_product_p: ^clt$data_value,
      i: rat$subproduct_count,
      j: 0 .. rac$max_additional_products,
      group_name: ost$name,
      subproduct_pointers: rat$subproduct_info_pointers;


    current_product_p := excluded_product_list_p;
    WHILE current_product_p <> NIL DO

    /main/
      FOR i := 1 TO UPPERBOUND (installation_control_record.subproduct_processing_records_p^) DO
        subproduct_pointers := installation_control_record.subproduct_processing_records_p^ [i].
              subproduct_info_pointers;

        IF subproduct_pointers.attributes_p^.subproduct_type <>
              installation_control_record.processing_header_p^.command_compatible_type THEN
          { Skip the subproduct when type is not installable by the executing command.
          CYCLE /main/;
        IFEND;

        IF (current_product_p^.element_value^.name_value = subproduct_pointers.attributes_p^.
              licensed_product) OR (current_product_p^.element_value^.name_value =
              subproduct_pointers.attributes_p^.name) THEN

          { The current exclude product name references this subproduct.
          { The subproduct will be excluded.

          installation_control_record.subproduct_processing_records_p^ [i].product_reference := rac$excluded;
          add_to_licensed_prod_references (rac$excluded, subproduct_pointers.attributes_p^.licensed_product,
                licensed_product_references);

        ELSE {check for group name}
          group_name (1, * ) := rac$group_designator;
          group_name (clp$trimmed_string_size (rac$group_designator) + 1, * ) :=
                current_product_p^.element_value^.name_value (1,
                clp$trimmed_string_size (current_product_p^.element_value^.name_value));

        /group_check/
          FOR j := 1 TO UPPERBOUND (subproduct_pointers.attributes_p^.additional_products) DO
            IF group_name = subproduct_pointers.attributes_p^.additional_products [j] THEN

              { The current exclude product name references this subproduct.
              { The subproduct will be excluded.

              installation_control_record.subproduct_processing_records_p^ [i].product_reference :=
                    rac$excluded;
              add_to_licensed_prod_references (rac$excluded, subproduct_pointers.attributes_p^.
                    licensed_product, licensed_product_references);

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

        IFEND;
      FOREND /main/;

      current_product_p := current_product_p^.link;
    WHILEND;

  PROCEND process_excluded_product_list;

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

{ PURPOSE:
{   This procedure processes the product list as the keyword ALL.
{
{ DESIGN:
{   Each subproduct known to the packing list is examined.  Subproducts of
{   type correction are skipped when the installation command is
{   INSTALL_PRODUCT.  Subproducts of type release are skipped when the
{   installation command is INSTALL_CORRECTION.  Those subproducts not
{   skipped and set to be automatically installed become selection
{   candidates.  The rules for selection are described in detail in the
{   procedure that actually performs the selection.
{
{   A validation error does not stop subproduct 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.
{
{   The subproduct's user reference will be defined as licensed product.
{
{   Those subproducts that were not selected and are of type release will be
{   assigned the task to have an IDB Directory record added (when not
{   already listed).  This guarantees that the entire licensed product is
{   documented in the IDB Directory even when some of it's associated
{   subproducts are not actually installed.  (Incidently, this means that a
{   licensed product that is not automatically installed will have records
{   added to the IDB Directory.)
{
{ NOTES:
{   The COMMAND_COMPATIBLE_SOFTWARE boolean indicates whether or not the
{   packing list (used in processing the installation event) contained
{   subproducts that were compatible with the originating command.  That is,
{   when called by INSTALL_PRODUCT, the packing list contains subproducts of
{   type release, and when called by INSTALL_CORRECTION, the packing list
{   contains subproducts of type correction.
{

  PROCEDURE process_product_list_all
    (    force_reinstall: boolean;
         directory_pointers: rat$idb_directory_pointers;
         installation_tasks: rat$task_selections;
     VAR installation_control_record {input, output} : rat$installation_control_record;
     VAR status: ost$status);


    VAR
      auto_install: boolean,
      command_compatible_software: boolean,
      i: rat$subproduct_count,
      ignore_licensed_prod_references: rat#licensed_product_references,
      software_to_install: boolean,
      subproduct_pointers: rat$subproduct_info_pointers,
      subproduct_selected: boolean,
      subproduct_validation_error: boolean,
      validation_errors_occurred: boolean;


    status.normal := TRUE;
    command_compatible_software := FALSE;
    ignore_licensed_prod_references.list_p := NIL;
    software_to_install := FALSE;
    validation_errors_occurred := FALSE;


  /main/
    FOR i := 1 TO UPPERBOUND (installation_control_record.subproduct_processing_records_p^) DO
      subproduct_pointers := installation_control_record.subproduct_processing_records_p^ [i].
            subproduct_info_pointers;

      IF subproduct_pointers.attributes_p^.subproduct_type <>
            installation_control_record.processing_header_p^.command_compatible_type THEN
        { Skip the subproduct when type is not compatible with the originating command.
        CYCLE /main/;
      IFEND;

      command_compatible_software := TRUE;
      subproduct_validation_error := FALSE;
      subproduct_selected := FALSE;

      IF installation_control_record.packing_list_pointers.order_medium = rac$tape THEN
        auto_install := installation_control_record.packing_list_pointers.tape_subproduct_indexer_p^ [i].
              auto_install;
      ELSE  { order_medium = rac$disk }
        auto_install := installation_control_record.packing_list_pointers.disk_subproduct_indexer_p^ [i].
              auto_install;
      IFEND;

      IF auto_install AND (installation_control_record.subproduct_processing_records_p^ [i].
            product_reference <> rac$excluded) THEN

        process_subproduct_candidate (subproduct_pointers.attributes_p^.licensed_product,
              rac$licensed_product, force_reinstall, directory_pointers, subproduct_pointers.attributes_p,
              subproduct_selected);

        IF subproduct_selected THEN
          register_subproduct_selection (rac$licensed_product,
                installation_control_record.packing_list_pointers, installation_tasks, i,
                installation_control_record.processing_header_p^.installation_defaults.system_catalog,
                installation_control_record.medium_processing_records_p,
                installation_control_record.subproduct_processing_records_p^ [i],
                ignore_licensed_prod_references, installation_control_record.processing_seq_p,
                subproduct_validation_error, status);
          IF NOT status.normal THEN
            RETURN;
          IFEND;
        IFEND;

        software_to_install := (software_to_install OR subproduct_selected);
        validation_errors_occurred := (validation_errors_occurred OR subproduct_validation_error);

      IFEND;

      IF (NOT subproduct_selected) AND (subproduct_pointers.attributes_p^.subproduct_type = rac$release) THEN
        installation_control_record.subproduct_processing_records_p^ [i].
              task_set := $rat$task_selections [rac$update_directory_task];
      IFEND;
    FOREND /main/;

    IF validation_errors_occurred THEN
      osp$set_status_abnormal ('RA', rae$validation_errors_occurred, 'PRODUCT', status);
    ELSEIF NOT software_to_install THEN
      IF command_compatible_software THEN
        osp$set_status_abnormal ('RA', rae$no_software_to_install, '', status);
      ELSE
        osp$set_status_abnormal ('RA', rae$incompatible_software,
              rav$subproduct_type [installation_control_record.processing_header_p^.command_compatible_type],
              status);
        osp$append_status_parameter (osc$status_parameter_delimiter,
              installation_control_record.processing_header_p^.
              packing_list_name (1, clp$trimmed_string_size (installation_control_record.processing_header_p^.
              packing_list_name)), status);
      IFEND;
    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:
{   The COMMAND_COMPATIBLE_SOFTWARE boolean indicates whether or not the
{   packing list (used in processing the installation event) contained
{   subproducts that were compatible with the originating command.  That is,
{   when called by INSTALL_PRODUCT, the packing list contains subproducts of
{   type release, and when called by INSTALL_CORRECTION, the packing list
{   contains subproducts of type correction.
{

  PROCEDURE process_product_list_names
    (    product_list_p: ^clt$data_value;
         force_reinstall: boolean;
         directory_pointers: rat$idb_directory_pointers;
         installation_tasks: rat$task_selections;
     VAR licensed_product_references {input, output} : rat#licensed_product_references;
     VAR installation_control_record {input, output} : rat$installation_control_record;
     VAR status: ost$status);


    VAR
      command_compatible_software: boolean,
      current_product_list_p: ^clt$data_value,
      subproducts_to_install: boolean,
      validation_errors_occurred: boolean;


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

    current_product_list_p := product_list_p;
    WHILE current_product_list_p <> NIL DO

      process_product_name (current_product_list_p^.element_value^.name_value, force_reinstall,
            directory_pointers, installation_tasks, licensed_product_references,
            installation_control_record, validation_errors_occurred, command_compatible_software,
            subproducts_to_install, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

      current_product_list_p := current_product_list_p^.link;
    WHILEND;

    IF validation_errors_occurred THEN
      osp$set_status_abnormal ('RA', rae$validation_errors_occurred, 'PRODUCT', status);
      RETURN;
    ELSEIF NOT subproducts_to_install THEN
      IF command_compatible_software THEN
        osp$set_status_abnormal ('RA', rae$no_software_to_install, '', status);
      ELSE
        osp$set_status_abnormal ('RA', rae$incompatible_software,
              rav$subproduct_type [installation_control_record.processing_header_p^.command_compatible_type],
              status);
        osp$append_status_parameter (osc$status_parameter_delimiter,
              installation_control_record.processing_header_p^.
              packing_list_name (1, clp$trimmed_string_size (installation_control_record.processing_header_p^.
              packing_list_name)), status);
      IFEND;
      RETURN;
    IFEND;

    process_licnsed_prod_references (licensed_product_references, installation_control_record);

  PROCEND process_product_list_names;

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

{ PURPOSE:
{   This procedure processes a single product name from the product list
{   against the subproduct list from the packing list.
{
{ DESIGN:
{   Each subproduct known to the packing list is examined.  Subproducts of
{   type correction are skipped when the installation command is
{   INSTALL_PRODUCT.  Subproducts of type release are skipped when the
{   installation command is INSTALL_CORRECTION.  Those subproducts not
{   skipped and referenced by the user (using the product list) either
{   directly (by subproduct name) or indirectly (by licensed product or
{   group name) become candidates for selection.  The rules for selection
{   are described in detail in the procedure that actually performs the
{   selection.
{
{   A licensed product reference is assumed when a product list name matches
{   the names of both a subproduct and a licensed product.  This means all
{   of the licensed product's subproducts are processed.
{
{   If any of the input list is determined to be not valid a validation error
{   is displayed and the validation error flag is set.
{
{ NOTES:
{

  PROCEDURE process_product_name
    (    product_name: ost$name;
         force_reinstall: boolean;
         directory_pointers: rat$idb_directory_pointers;
         installation_tasks: rat$task_selections;
     VAR licensed_product_references {input, output} : rat#licensed_product_references;
     VAR installation_control_record {input, output} : rat$installation_control_record;
     VAR validation_errors_occurred {input, output} : boolean;
     VAR command_compatible_software {input, output} : boolean;
     VAR subproducts_to_install: boolean;
     VAR status: ost$status);


    VAR
      auto_install: boolean,
      group_name: ost$name,
      i: rat$subproduct_count,
      ignore_status: ost$status,
      j: 0 .. rac$max_additional_products,
      local_status: ost$status,
      product_reference: rat$product_references,
      some_subproducts_rejected: boolean,
      some_subproducts_selected: boolean,
      subproduct_pointers: rat$subproduct_info_pointers,
      subproduct_selected: boolean,
      subproduct_validation_error: boolean;


    status.normal := TRUE;
    product_reference := rac$not_referenced;
    some_subproducts_rejected := FALSE;
    some_subproducts_selected := FALSE;

  /main/
    FOR i := 1 TO UPPERBOUND (installation_control_record.subproduct_processing_records_p^) DO
      subproduct_pointers := installation_control_record.subproduct_processing_records_p^ [i].
            subproduct_info_pointers;

      IF subproduct_pointers.attributes_p^.subproduct_type <>
            installation_control_record.processing_header_p^.command_compatible_type THEN
        { Skip the subproduct when type is not installable by the executing command.
        CYCLE /main/;
      IFEND;

      command_compatible_software := TRUE;
      subproduct_validation_error := FALSE;
      subproduct_selected := FALSE;

      IF installation_control_record.packing_list_pointers.order_medium = rac$tape THEN
        auto_install := installation_control_record.packing_list_pointers.tape_subproduct_indexer_p^ [i].
              auto_install;
      ELSE  { order_medium = rac$disk }
        auto_install := installation_control_record.packing_list_pointers.disk_subproduct_indexer_p^ [i].
              auto_install;
      IFEND;

      IF product_name = subproduct_pointers.attributes_p^.licensed_product THEN
        product_reference := rac$licensed_product;
        IF installation_control_record.subproduct_processing_records_p^ [i].product_reference <>
              rac$excluded THEN

          {  The subproduct is only processed when auto install is true or the licensed product
          {  and subproduct names match.  When auto install is false, a task is assigned to add
          {  an entry to the directory if not already present.

          IF auto_install OR (subproduct_pointers.attributes_p^.licensed_product =
                subproduct_pointers.attributes_p^.name) THEN
            process_subproduct_candidate (product_name, product_reference, force_reinstall,
                  directory_pointers, subproduct_pointers.attributes_p, subproduct_selected);
            some_subproducts_rejected := (some_subproducts_rejected OR NOT subproduct_selected);
          ELSE {not auto install}
            installation_control_record.subproduct_processing_records_p^ [i].task_set :=
                  $rat$task_selections [rac$update_directory_task];
          IFEND;
        IFEND;

      ELSEIF product_name = subproduct_pointers.attributes_p^.name THEN
        product_reference := rac$subproduct;
        IF installation_control_record.subproduct_processing_records_p^ [i].product_reference <>
              rac$excluded THEN
          process_subproduct_candidate (product_name, product_reference, force_reinstall,
                directory_pointers, subproduct_pointers.attributes_p, subproduct_selected);
          some_subproducts_rejected := (some_subproducts_rejected OR NOT subproduct_selected);
        IFEND;

      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_pointers.attributes_p^.additional_products) DO
          IF group_name = subproduct_pointers.attributes_p^.additional_products [j] THEN
            product_reference := rac$group;
            IF auto_install AND (installation_control_record.subproduct_processing_records_p^ [i].
                  product_reference <> rac$excluded) THEN
              process_subproduct_candidate (product_name, product_reference, force_reinstall,
                    directory_pointers, subproduct_pointers.attributes_p, subproduct_selected);
              some_subproducts_rejected := (some_subproducts_rejected OR NOT subproduct_selected);
            IFEND;

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

      IFEND;

      IF subproduct_selected THEN
        register_subproduct_selection (product_reference, installation_control_record.packing_list_pointers,
              installation_tasks, i, installation_control_record.processing_header_p^.installation_defaults.
              system_catalog, installation_control_record.medium_processing_records_p,
              installation_control_record.subproduct_processing_records_p^ [i], licensed_product_references,
              installation_control_record.processing_seq_p, subproduct_validation_error, status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;
      IFEND;

      some_subproducts_selected := (some_subproducts_selected OR subproduct_selected);
      validation_errors_occurred := (validation_errors_occurred OR subproduct_validation_error);

    FOREND /main/;


    IF product_reference = rac$not_referenced THEN
      osp$set_status_abnormal ('RA', rae$unknown_product_name,
            product_name (1, clp$trimmed_string_size (product_name)), local_status);
      osp$generate_message (local_status, ignore_status);
      validation_errors_occurred := TRUE;
    ELSEIF some_subproducts_rejected THEN
      display_product_rejected_msg (some_subproducts_selected, product_reference, product_name);
    IFEND;

    subproducts_to_install := (subproducts_to_install OR some_subproducts_selected);

  PROCEND process_product_name;

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

{ PURPOSE:
{   This procedure processes the indirect licensed product references made
{   by a specified subproduct or group name or the excluded product list.
{
{ DESIGN:
{   When a subproduct or group from the product list indirectly references
{   a licensed product that was not directly specified, the IDB directory
{   must be updated to record the licensed product reference.
{
{   At this time the list of indirect licensed product references are
{   checked and all subproducts associated with the licensed product, that
{   are not already selected will have their task list set to update directory.
{
{ NOTES:
{

  PROCEDURE process_licnsed_prod_references
    (    licensed_product_references: rat#licensed_product_references;
     VAR installation_control_record {input, output} : rat$installation_control_record);


    VAR
      i: rat$subproduct_count,
      j: rat$subproduct_count;


    FOR i := 1 TO licensed_product_references.list_length DO

      IF licensed_product_references.list_p^ [i].selected THEN

        FOR j := 1 TO UPPERBOUND (installation_control_record.subproduct_processing_records_p^) DO

          IF (licensed_product_references.list_p^ [i].name =
                installation_control_record.subproduct_processing_records_p^ [j].subproduct_info_pointers.
                attributes_p^.licensed_product) AND (installation_control_record.
                subproduct_processing_records_p^ [j].subproduct_info_pointers.attributes_p^.subproduct_type =
                rac$release) THEN

            IF installation_control_record.subproduct_processing_records_p^ [j].task_set =
                  $rat$task_selections [] THEN

              installation_control_record.subproduct_processing_records_p^ [j].task_set :=
                    $rat$task_selections [rac$update_directory_task];

            IFEND;
          IFEND;
        FOREND;
      IFEND;
    FOREND;

  PROCEND process_licnsed_prod_references;

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

{ PURPOSE:
{   This procedure checks the IDB Directory to determine if the subproduct
{   candidate should be selected.  A boolean is returned based on the
{   results of the check.
{
{ DESIGN:
{   The following rules cause a subproduct to be rejected:
{
{     1.  If no directory record for the subproduct exists or the
{         subproduct is not active and the subproduct level to be
{         installed is a correction.
{
{     2.  If the subproduct level to be installed is a correction and
{         its correction base level does not match that of the
{         correction base for the subproduct in the directory.
{
{     3.  If the subproduct level to be installed is already active and
{         there is no other level deferred.
{
{     4.  If the subproduct level to be installed is already deferred.
{
{     5.  If the subproduct level to be installed matches the correction
{         base level for the subproduct in the directory.  This is to
{         prevent release levels from being re-installed after corrections
{         have been installed.
{
{   An exception to the rules occurs the force_reinstall parameter is
{   specified.  This causes rules #3, #4 and #5 to be ignored.
{
{   When a subproduct is determined not selected, the reason for the
{   rejection is displayed in an informative message to the job log.
{
{ NOTES:
{   All the "level checks" are done using the SIF identifier, except
{   when a correction was generated using DEFINE_SUBPRODUCT.  In this
{   case the CORRECTION_BASE_LEVEL_SIF_IDENTIFIER is not available, so
{   that the actual level fields are compared.
{

  PROCEDURE process_subproduct_candidate
    (    product_name: ost$name;
         product_reference: rat$product_references;
         force_reinstall: boolean;
         directory_pointers: rat$idb_directory_pointers;
         subproduct_attributes_p: ^rat$subproduct_attributes;
     VAR subproduct_selected: boolean);


    VAR
      directory_record_p: ^rat$directory_record,
      ignore_status: ost$status,
      operator_message: string (256),
      operator_message_length : integer,
      response_from_operator: ost$string,
      subproduct_can_be_installed: boolean;


    subproduct_can_be_installed := TRUE;

    rap$locate_directory_record (subproduct_attributes_p^.name, subproduct_attributes_p^.licensed_product,
          directory_pointers, directory_record_p);

    IF directory_record_p = NIL THEN

      IF subproduct_attributes_p^.subproduct_type = rac$correction THEN

        { Subproduct correction cannot be installed, subproduct currently not active.

        subproduct_can_be_installed := FALSE;
        display_subproduct_rejected_msg (subproduct_attributes_p^.name,
              subproduct_attributes_p^.licensed_product, rae$cannot_install_correction, product_name,
              product_reference);

      IFEND;

    ELSE {directory record found}

      IF (subproduct_attributes_p^.subproduct_type = rac$correction) AND
            (directory_record_p^.active_information.installation_identifier = rac$not_installed) THEN

        { Subproduct correction cannot be installed, subproduct currently not active.

        subproduct_can_be_installed := FALSE;
        display_subproduct_rejected_msg (subproduct_attributes_p^.name,
              subproduct_attributes_p^.licensed_product, rae$cannot_install_correction, product_name,
              product_reference);

      ELSEIF (subproduct_attributes_p^.subproduct_type = rac$correction) AND
            (((subproduct_attributes_p^.correction_base_sif_identifier <> osc$null_name) AND
            (directory_record_p^.corrective_base_information.installation_identifier <> rac$not_installed) AND
            (subproduct_attributes_p^.correction_base_sif_identifier <>
            directory_record_p^.corrective_base_information.sif_identifier)) OR
            ((subproduct_attributes_p^.correction_base_sif_identifier = osc$null_name) AND
            (subproduct_attributes_p^.correction_base_level <>
            directory_record_p^.corrective_base_information.subproduct_level))) THEN

        { Subproduct correction is not applicable to subproduct currently active.

        subproduct_can_be_installed := FALSE;
        display_subproduct_rejected_msg (subproduct_attributes_p^.name,
              subproduct_attributes_p^.licensed_product, rae$correction_not_applicable, product_name,
              product_reference);
        stringrep(operator_message, operator_message_length, 'Subproduct ',
              subproduct_attributes_p^.name (1, clp$trimmed_string_size(subproduct_attributes_p^.name)),
              ' of ', subproduct_attributes_p^.licensed_product (1,
              clp$trimmed_string_size(subproduct_attributes_p^.licensed_product)),
              ' not installed due to release level of subproduct in the IDB directory',
              ' not matching the level required for the correction.  See job log.');
        ofp$send_operator_message(operator_message (1, operator_message_length), ofc$system_operator,
              {acknowledgement_allowed =} TRUE, ignore_status);
        ofp$receive_operator_response (ofc$system_operator, osc$wait,
              response_from_operator, ignore_status);
      ELSEIF NOT force_reinstall THEN

        { Force_reinstall is not used.

        IF (directory_record_p^.active_information.installation_identifier <> rac$not_installed) AND
              (subproduct_attributes_p^.sif_identifier = directory_record_p^.active_information.
              sif_identifier) AND ((directory_record_p^.deferred_information.installation_identifier =
              rac$not_installed) OR (subproduct_attributes_p^.sif_identifier =
              directory_record_p^.deferred_information.sif_identifier)) THEN

          { Subproduct at this level is already installed active and no other level deferred.

          subproduct_can_be_installed := FALSE;
          display_subproduct_rejected_msg (subproduct_attributes_p^.name,
                subproduct_attributes_p^.licensed_product, rae$subproduct_installed, product_name,
                product_reference);

        ELSEIF (directory_record_p^.deferred_information.installation_identifier <> rac$not_installed) AND
              (subproduct_attributes_p^.sif_identifier = directory_record_p^.deferred_information.
              sif_identifier) THEN

          { Subproduct at this level is already installed deferred.

          subproduct_can_be_installed := FALSE;
          display_subproduct_rejected_msg (subproduct_attributes_p^.name,
                subproduct_attributes_p^.licensed_product, rae$subproduct_already_deferred, product_name,
                product_reference);

        ELSEIF (directory_record_p^.corrective_base_information.installation_identifier <> rac$not_installed)
              AND (subproduct_attributes_p^.sif_identifier = directory_record_p^.corrective_base_information.
              sif_identifier) THEN

          { Subproduct at this level is already installed as the corrective base.

          subproduct_can_be_installed := FALSE;
          display_subproduct_rejected_msg (subproduct_attributes_p^.name,
                subproduct_attributes_p^.licensed_product, rae$subp_installed_as_corr_base, product_name,
                product_reference);

        IFEND;
      IFEND;
    IFEND;

    subproduct_selected := subproduct_can_be_installed;

  PROCEND process_subproduct_candidate;

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

{ PURPOSE:
{   This procedure registers the subproduct as selected.  Any validation errors
{   encountered are flagged.
{
{ DESIGN:
{   This procedure validates that the subproduct's installation path is
{   defined.  A validation error flag is returned when the path is not.
{
{   The installation task list and user reference (ie.  product type) are
{   assigned to the subproduct processing record.  The task list will cause the
{   subproduct to be processed accordingly when processing actually begins.
{
{   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.
{
{   When the user reference is by subproduct or group name, the licensed
{   product associated with that subproduct is added to a reference list.
{
{   The subproduct's installation catalog is also assembled and stored in the
{   processing sequence.
{
{ NOTES:
{

  PROCEDURE register_subproduct_selection
    (    product_reference: rat$product_references;
         packing_list_pointers: rat$packing_list_pointers;
         installation_tasks: rat$task_selections;
         subproduct_index: rat$subproduct_count;
         system_catalog: rat$path;
         medium_processing_records_p { input, output } : ^rat$medium_processing_records;
     VAR subproduct_processing_record { input, output } : rat$subp_processing_record;
     VAR licensed_product_references { input, output } : rat#licensed_product_references;
     VAR processing_seq_p: ^rat$processing_sequence;
     VAR subproduct_validation_error: boolean;
     VAR status: ost$status);


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

    validate_installation_path (subproduct_processing_record.subproduct_info_pointers.attributes_p^.name,
          subproduct_processing_record.subproduct_info_pointers.attributes_p^.subproduct_type,
          subproduct_processing_record.subproduct_info_pointers.attributes_p^.licensed_product,
          subproduct_processing_record.subproduct_info_pointers.attributes_p^.installation_path.
          path_container_index, subproduct_processing_record.subproduct_info_pointers.path_container_p,
          subproduct_validation_error);

    IF subproduct_validation_error THEN
      RETURN;
    IFEND;

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

    subproduct_processing_record.task_set := installation_tasks;
    subproduct_processing_record.product_reference := product_reference;

    { Increment the appropriate medium processing record's subproduct count.

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

    { Call ADD_TO_LICENSED_PROD_REFERENCES to updated the licensed product references list, when not nil.
    { How the product was referenced determines what occurs in that procedure.

    IF licensed_product_references.list_p <> NIL THEN
      add_to_licensed_prod_references (product_reference, subproduct_processing_record.
            subproduct_info_pointers.attributes_p^.licensed_product, licensed_product_references);
    IFEND;

    { Assign the installation path for the subproduct.

    rap$assemble_installation_path( system_catalog, subproduct_processing_record.subproduct_info_pointers,
         rac$installation_path, subproduct_processing_record.installation_catalog_p, processing_seq_p,
         status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    subproduct_processing_record.installation_catalog_rel_p :=
          #REL (subproduct_processing_record.installation_catalog_p, processing_seq_p^);

  PROCEND register_subproduct_selection;

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

{ PURPOSE:
{   This procedure renames the existing incompatible directory
{   so that a new directory can be created in it's place.
{
{ DESIGN:
{
{ NOTES:
{

  PROCEDURE replace_incompatible_directory
    (    installation_database: rat$path;
     VAR directory_pointers: rat$idb_directory_pointers;
     VAR directory_fid: amt$file_identifier;
     VAR directory_file_opened: boolean;
     VAR status: ost$status);


    CONST
      no_password = '                               ';

    VAR
      cycle_reference: fst$cycle_reference,
      cycle_selector: clt$cycle_selector,
      directory: rat$path,
      directory_path_p: ^pft$path,
      fs_directory_path: string (fsc$max_path_size),
      ignore_open_position: fst$open_position,
      ignore_status: ost$status,
      length: integer,
      local_status: ost$status,
      new_directory: rat$path,
      new_name: array [1 .. 1] OF pft$change_descriptor,
      number_of_path_elements: fst$number_of_path_elements;


    status.normal := TRUE;

    { Convert the directory path to the correct format for using pfp$change interface.

    STRINGREP (directory.path, directory.size, installation_database.path (1, installation_database.size),
          '.', rac$idb_directory_name);

    pfp$convert_string_to_fs_path (directory.path (1, directory.size), fs_directory_path,
          number_of_path_elements, cycle_reference, ignore_open_position, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    PUSH directory_path_p: [1 .. number_of_path_elements];
    pfp$convert_fs_path_to_pf_path (fs_directory_path, directory_path_p, cycle_reference, cycle_selector,
          status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    { Change directory name, this will allow a new directory to be created in it's place.

    new_name [1].change_type := pfc$pf_name_change;
    new_name [1].pfn (1, * ) := rac$idb_directory_name;
    STRINGREP (new_name [1].pfn, length, rac$idb_directory_name, '_OLD');

    pfp$change (directory_path_p^, cycle_selector.value, no_password, new_name, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    { Display message conveying name change of the old directory.

    STRINGREP (new_directory.path, new_directory.size, installation_database.
          path (1, installation_database.size), '.', new_name
          [1].pfn (1, clp$trimmed_string_size (new_name [1].pfn)));

    osp$set_status_abnormal ('RA', rae$directory_moved, '', local_status);
    osp$append_status_file (osc$status_parameter_delimiter, new_directory.path (1, new_directory.size),
          local_status);
    osp$generate_log_message ($pmt$ascii_logset [pmc$job_log], local_status, ignore_status);

    { Call access_directory_for_validation to create a new directory.

    access_directory_for_validation (installation_database, directory_pointers, directory_fid,
          directory_file_opened, status);

  PROCEND replace_incompatible_directory;

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

{ PURPOSE:
{   This procedure validates that the subproduct's installation path is
{   defined.  A message is displayed to the job log when the path is not
{   defined.  In either case a boolean is returned with the validation
{   results.
{
{ DESIGN:
{   The verify is only of value when the path is user definable and the
{   subproduct is type release.
{
{ NOTES:
{

  PROCEDURE validate_installation_path
    (    subproduct_name: rat$subproduct_name;
         subproduct_type: rat$subproduct_type;
         licensed_product: rat$licensed_product;
         path_container_index: rat$path_container_index;
         path_container_p: ^rat$path_container;
     VAR path_validation_error: boolean);


    VAR
      ignore_status: ost$status,
      local_status: ost$status;


    { Determine whether or not the installation path is defined when the subproduct is
    { for a release.

    path_validation_error := FALSE;

    IF (subproduct_type = rac$release) AND ((path_container_p^ [path_container_index] =
          rac$undefined_inst_path_element) OR (path_container_p^ [path_container_index + 1] =
          rac$undefined_inst_path_element)) THEN

      { Either the family and/or user catalog names are not defined.

      path_validation_error := TRUE;

      osp$set_status_abnormal ('RA', rae$install_path_not_defined,
            subproduct_name (1, clp$trimmed_string_size (subproduct_name)), local_status);
      osp$append_status_parameter (osc$status_parameter_delimiter,
            licensed_product (1, clp$trimmed_string_size (licensed_product)), local_status);
      osp$generate_message (local_status, ignore_status);

    IFEND;

  PROCEND validate_installation_path;

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

{ PURPOSE:
{   This interface validates the product list specified on the installation
{   command call.
{
{ DESIGN:
{   Validation of the product list involves, first, verifying that the
{   licensed product, subproduct and/or group names specified by the product
{   list are known to the packing list used for this installation event.
{   Second, verifying that the subproducts referenced by the product list
{   names can be installed.  And third, setting the installation tasks for
{   those subproducts selected and approved for installation.  The
{   optional exclude products list is taken into account during validation.
{
{   Generally, the user will reference the subproducts by the licensed
{   product names.  This is how INSS must relate information back to the
{   user.  When the user references a subproduct by subproduct or group
{   name, the licensed product associated with that subproduct is added to a
{   reference list.  After selection has completed the licensed product
{   references list is processed.  A task is assigned that will add an IDB
{   Directory record (if not already in the directory) to all non-selected
{   subproducts belonging to the licensed products from the references list.
{   This guarantees that the entire licensed product is documented in the
{   IDB Directory even when some of it's associated subproducts are not
{   actually installed.
{
{   The algorithm for this procedure is as follows:
{
{   The subproducts specified by the exclude product list (either directly
{   or indirectly) are set to be excluded from further consideration.  The
{   licensed product names referenced by the excluded subproducts are added
{   to the licensed product references list (with the selected field is not
{   set).
{
{   The appropriate product list processing procedure is called (key ALL or
{   names).  When the product list is the keyword ALL, the licensed product
{   reference list is not used.  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.
{
{   (The IDB Directory is used to determine if the subproducts referenced by
{   the product list are installable.)
{
{ NOTES:
{

  PROCEDURE validate_product_list
    (    product_list_p: ^clt$data_value;
         excluded_product_list_p: ^clt$data_value;
         force_reinstall: boolean;
         directory_pointers: rat$idb_directory_pointers;
         installation_tasks: rat$task_selections;
     VAR installation_control_record {input, output} : rat$installation_control_record;
     VAR status: ost$status);


    VAR
      current_p: ^clt$data_value,
      licensed_product_references: rat#licensed_product_references;


    status.normal := TRUE;

    licensed_product_references.list_length := 0;
    PUSH licensed_product_references.list_p: [1 .. UPPERBOUND (installation_control_record.
          subproduct_processing_records_p^)];

    { Process the excluded product list.

    process_excluded_product_list (excluded_product_list_p, licensed_product_references,
          installation_control_record);

    { 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 (force_reinstall, directory_pointers, installation_tasks,
          installation_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, force_reinstall, directory_pointers, installation_tasks,
            licensed_product_references, installation_control_record, status);

    IFEND;

  PROCEND validate_product_list;
MODEND ram$validate_for_installation;
