?? RIGHT := 110 ??
?? NEWTITLE := 'INSTALL_SOFTWARE Utility: ACTIVATE_PRODUCT Subcommand.' ??
MODULE ram$activate_product_command;

{ PURPOSE:
{   This module contains the command interface that activates deferred
{   products.
{
{ DESIGN:
{   The compiled module resides in RAF$LIBRARY.
{
{ NOTES:
{

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc rac$command_log_name
*copyc rac$control_job_identifier
*copyc rac$control_file_name
*copyc rac$not_installed
*copyc rae$install_software_cc
*copyc rat$idb_directory_pointers
*copyc rat$installation_control_record
?? POP ??
*copyc clp$evaluate_parameters
*copyc clp$trimmed_string_size
*copyc clp$include_line
*copyc fsp$close_file
*copyc mmp$create_scratch_segment
*copyc mmp$delete_scratch_segment
*copyc osp$append_status_parameter
*copyc osp$disestablish_cond_handler
*copyc osp$establish_block_exit_hndlr
*copyc osp$generate_log_message
*copyc osp$set_status_abnormal
*copyc pmp$get_compact_date_time
*copyc pmp$log_ascii
*copyc rap$access_directory_for_read
*copyc rap$assign_install_identifier
*copyc rap$display_job_log_to_cmd_log
*copyc rap$init_processing_seq_fr_file
*copyc rap$perform_installation_steps
*copyc rav$installation_defaults
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

{   The following types are used to define the activation lists.  The
{   activation lists organize the subproducts (to be activated) by the
{   installation control files (ICFs) originally used to defer them.  One
{   activation list is created for each ICF used.

{   An activation list consists of a header record that points to a linked
{   list of subproduct activation records.  Each record defines a subproduct
{   to be activated.
{
{   The activation lists are linked together through another field in the
{   header record.
{

  TYPE
    rat#activation_list_header = record
      installation_identifier: rat$installation_identifier,
      number_of_subproducts: rat$subproduct_count,
      first_subproduct_p: ^rat#subp_activation_record,
      last_subproduct_p: ^rat#subp_activation_record,
      next_activation_list_p: ^rat#activation_list_header,
    recend;

  TYPE
    rat#subp_activation_record = record
      name: rat$subproduct_name,
      processing_records_index: rat$subproduct_count,
      next_subproduct_p: ^rat#subp_activation_record
    recend;

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

{ PURPOSE:
{   This command interface activates the specified licensed product(s)
{   and/or subproduct(s).
{
{ DESIGN:
{   Only subproducts installed deferred can be activated.  The complete
{   installation process is divided into the following major steps:
{
{     1.  RECONCILING CYCLE CONFLICTS
{     2.  LOADING THE SUBPRODUCTS
{     3.  CORRECTING THE SUBPRODUCTS
{     4.  STAGING THE SUBPRODUCTS
{     5.  ACTIVATING THE SUBPRODUCTS
{     6.  EXECUTING THE INSTALLER PROCEDURES
{     7.  UPDATING THE IDB DIRECTORY
{     8.  DELETING PREVIOUS CYCLES
{
{   A subproduct that is deferred has been through steps 1-4 and 7.  To
{   activate the deferred subproduct steps 5 through 8 are performed.
{
{   The IDB Directory is used to determine which subproducts are deferred
{   and therefore which subproducts can be activated.  The directory also
{   contains the installation identifier that is used to locate the
{   installation control file (ICF) used to install each deferred
{   subproduct.  The same ICF will be used to activate the subproduct that
{   was used to install the deferred subproduct.  It is possible for there
{   to be more than one ICF to activate all the deferred subproducts.
{
{   To prepare for activation, activation lists are assembled (see type
{   description above) .  The activation lists contain lists of subproducts
{   to be activated.  Each subproduct is grouped according to the ICF used
{   to install that subproduct as deferred.  The SUBPRODUCT parameter allows
{   the caller of this command to select a subset of the deferred
{   subproducts to activate.  This is validated against the directory.
{
{   An installation identifier is created and displayed for this
{   installation event.
{
{   The activation lists are then used to activate the requested subproducts
{   from one ICF at a time until all the involved ICFs have been processed.
{
{   The failure of one ICF (ICF missing for example) nor the failure of one
{   subproduct of an ICF will prevent other ICF's from being processed.
{
{   The job log will be written to a permanent command log file under the
{   installation identifier catalog.  The job log is first displayed to
{   $null to set a LAST displayed mark.  After processing, the job log is
{   displayed starting from the last displayed mark.
{
{   The directory is taken from the installation database as defined by the
{   installation defaults.
{
{ NOTES:
{   Batch processing is not supported for ACTIVATE_PRODUCT at 1.4.1.  The
{   following discussion should be kept in mind if it is ever to be
{   implemented.  In INSTALL_PRODUCT and INSTALL_CORRECTION, batch
{   processing involves performing the installation steps only.  If this
{   concept were used in ACTIVATE_PRODUCT, the ICFs would have to be
{   re-written, which means having multiple ICFs for a single installation
{   event.  An alternative would be to execute a call to
{   ACTIVATE_PRODUCTS_FROM_ICF from batch.
{

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


{ PROCEDURE actp_pdt (
{   subproduct, subproducts, s: any of
{       key
{         all
{       keyend
{       list of name
{     anyend = $required
{   save_previous_cycles, spc: boolean = $optional
{   status)

?? PUSH (LISTEXT := ON) ??

    VAR
      pdt: [STATIC, READ, cls$declaration_section] record
        header: clt$pdt_header,
        names: array [1 .. 6] of clt$pdt_parameter_name,
        parameters: array [1 .. 3] of clt$pdt_parameter,
        type1: 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,
        recend,
        type2: record
          header: clt$type_specification_header,
        recend,
        type3: record
          header: clt$type_specification_header,
        recend,
      recend := [[1, [88, 9, 28, 15, 9, 19, 68], clc$command, 6, 3, 1, 0, 0, 0, 3, 'ACTP_PDT'],
            [['S                              ', clc$abbreviation_entry, 1],
            ['SAVE_PREVIOUS_CYCLES           ', clc$nominal_entry, 2],
            ['SPC                            ', clc$abbreviation_entry, 2],
            ['STATUS                         ', clc$nominal_entry, 3],
            ['SUBPRODUCT                     ', clc$nominal_entry, 1],
            ['SUBPRODUCTS                    ', clc$alias_entry, 1]], [
{ PARAMETER 1
      [5, 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$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$optional_parameter, 0, 0],
{ PARAMETER 3
      [4, 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$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]]]],
{ PARAMETER 2
      [[1, 0, clc$boolean_type]],
{ PARAMETER 3
      [[1, 0, clc$status_type]]];

?? POP ??

    CONST
      p$subproduct = 1,
      p$save_previous_cycles = 2,
      p$status = 3;

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


    VAR
      activation_lists_p: ^rat#activation_list_header,
      activation_lists_segment_ptr: amt$segment_pointer,
      current_activation_list_p: ^rat#activation_list_header,
      ignore_status: ost$status,
      installation_identifier: rat$installation_identifier,
      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 return the scratch segment
{   used for the activation list and display the job log to the command log
{   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 activation_lists_segment_ptr.sequence_pointer <> NIL THEN
        mmp$delete_scratch_segment (activation_lists_segment_ptr, ignore_status);
        activation_lists_segment_ptr.sequence_pointer := NIL;
      IFEND;

      rap$display_job_log_to_cmd_log (rav$installation_defaults.installation_logs, installation_identifier,
            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;

    installation_identifier := osc$null_name;
    clp$include_line ('$system.display_log do=last o=$null', TRUE, osc$null_name, ignore_status);

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

    osp$establish_block_exit_hndlr (^abort_handler);

  /main/
    BEGIN

      get_activation_lists (rav$installation_defaults.installation_database, pvt [p$subproduct].value,
            activation_lists_segment_ptr, activation_lists_p, status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;

      rap$assign_install_identifier (rac$activate_product, rav$installation_defaults.installation_logs,
            osc$null_name {packing list not used} , installation_identifier, status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;

      current_activation_list_p := activation_lists_p;

      WHILE current_activation_list_p <> NIL DO

        activate_subproducts_from_icf (current_activation_list_p, installation_identifier,
              pvt [p$save_previous_cycles], status);
        IF NOT status.normal THEN
          EXIT /main/;
        IFEND;

        current_activation_list_p := current_activation_list_p^.next_activation_list_p;
      WHILEND;

    END /main/;

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

    rap$display_job_log_to_cmd_log (rav$installation_defaults.installation_logs, installation_identifier,
          ignore_status);

    osp$disestablish_cond_handler;

  PROCEND rap$activate_product_command;

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

{ PURPOSE:
{   This procedure activates the subproducts from the activation list
{   associated with the specified installation control file (ICF).
{
{ DESIGN:
{   The installation control record is re-established for the execution of
{   the installation steps.  The installation control file contains a copy
{   of the processing sequence that was originally created for the deferred
{   installation.  The processing sequence is re-created from the
{   installation control file and the packing list is re-accessed if not
{   already contained in the processing sequence (at 1.4.1 the packing list
{   is always part of the processing sequence).  If the packing list is
{   accessed during re-creation of the installation control record it will
{   be closed by this procedure.
{
{   The saving of previous cycles is controlled by the value from the ICF
{   (set during the deferred installation) unless overridden by the
{   parameter on the ACTIVATE_PRODUCT command.
{
{   The global rav$installation_defaults are reset to the values from the
{   installation control file.  This will put the INSTALL_SOFTWARE
{   environment in agreement with the INSTALL_SOFTWARE environment
{   established when the subproducts were first installed deferred.
{
{   The subproducts to be activated are validated that their files are in
{   the staging cycle.
{
{   The subproduct task sets (in the processing sequence) are redefined.
{   Those not being activated are cleared.
{
{   The job identifier is re-established to guide the step processing.  The
{   job status record is also set up.  In this case a processing summary
{   file is not created, instead a local job status record used.
{
{   Once setup is complete the installation steps for the ICF are executed.
{
{ NOTES:
{   The installation defaults are set to those found in the ICF.  This will
{   allow activation to occur in an environment close to that used when the
{   subproducts were deferred.  These may vary from the values directly
{   associated with the current install software defaults.  The current
{   defaults are saved and then restored after activation for this ICF has
{   completed.
{
{   The job status record is established locally.  In other commands the job
{   status records are created as part of a processing summary file.
{
{   The distinction between a step and a task is that steps indicate
{   processing at the global level, where as, tasks relate to processing at
{   the subproduct level.
{
{   The scratch segment used for the processing sequence is created in
{   RAP$INIT_PROCESSING_SEQ_FR_FILE.
{

  PROCEDURE activate_subproducts_from_icf
    (    activation_list_p: ^rat#activation_list_header;
         installation_identifier: rat$installation_identifier;
         save_previous_cycles: clt$parameter_value;
     VAR status: ost$status);


    VAR
      ignore_status: ost$status,
      installation_control_file: rat$path,
      installation_control_record: rat$installation_control_record,
      local_status: ost$status,
      packing_list_fid: amt$file_identifier,
      packing_list_opened: boolean,
      processing_seq_segment_pointer: amt$segment_pointer,
      saved_installation_defaults: rat$installation_defaults;


?? 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
{   for the processing sequence and 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 processing_seq_segment_pointer.sequence_pointer <> NIL THEN
        mmp$delete_scratch_segment (processing_seq_segment_pointer, ignore_status);
        processing_seq_segment_pointer.sequence_pointer := NIL;
      IFEND;

      fsp$close_file (packing_list_fid, ignore_status);

      rav$installation_defaults := saved_installation_defaults;

    PROCEND abort_handler;

?? OLDTITLE, EJECT ??


    status.normal := TRUE;
    saved_installation_defaults := rav$installation_defaults;

    installation_control_record.job_identifier := rac$control_job_identifier;
    PUSH installation_control_record.job_status_record_p;

    processing_seq_segment_pointer.kind := amc$sequence_pointer;
    processing_seq_segment_pointer.sequence_pointer := NIL;
    packing_list_opened := FALSE;

    { Construct the installation control file path from the installation
    { identifier from the activation list header and default installation
    { logs catalog path.

    STRINGREP (installation_control_file.path, installation_control_file.size,
          rav$installation_defaults.installation_logs.path (1,
          rav$installation_defaults.installation_logs.size),
          '.', activation_list_p^.installation_identifier (1,
          clp$trimmed_string_size (activation_list_p^.installation_identifier)), '.', rac$control_file_name);

    osp$establish_block_exit_hndlr (^abort_handler);

  /main/
    BEGIN

      rap$init_processing_seq_fr_file (installation_control_file.path (1, installation_control_file.size),
            installation_control_record, processing_seq_segment_pointer, packing_list_fid,
            packing_list_opened, status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;

      installation_control_record.processing_header_p^.installation_command := rac$activate_product;

      IF save_previous_cycles.specified THEN
        installation_control_record.processing_header_p^.save_previous_cycles :=
              save_previous_cycles.value^.boolean_value.value;
      IFEND;

      rav$installation_defaults := installation_control_record.processing_header_p^.installation_defaults;

      establish_task_set (activation_list_p, installation_control_record);

      establish_processing_cntrls (installation_identifier, activation_list_p, installation_control_record,
            status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;

      rap$perform_installation_steps (installation_control_record, status);

    END /main/;

    rav$installation_defaults := saved_installation_defaults;

    IF packing_list_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 processing_seq_segment_pointer.sequence_pointer <> NIL THEN
      mmp$delete_scratch_segment (processing_seq_segment_pointer, local_status);
      processing_seq_segment_pointer.sequence_pointer := NIL;
      IF status.normal AND (NOT local_status.normal) THEN
        status := local_status;
      IFEND;
    IFEND;

    osp$disestablish_cond_handler;

  PROCEND activate_subproducts_from_icf;

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

{ PURPOSE:
{   This procedure adds the subproduct name to the activation list for the
{   ICF.  If the ICF does not yet have activation list, a new activation
{   list is started.
{
{ DESIGN:
{   The installation identifier is used to identifier one installation
{   control file from another.
{
{ NOTES:
{

  PROCEDURE add_to_activation_list
    (    subproduct_name: rat$subproduct_name;
         installation_identifier: rat$installation_identifier;
         processing_records_index: rat$subproduct_count;
     VAR activation_lists_segment_ptr {input, output} : amt$segment_pointer;
     VAR activation_lists_p: ^rat#activation_list_header;
     VAR status: ost$status);


    VAR
      current_activation_list_p: ^rat#activation_list_header,
      new_subproduct_activation_rec_p: ^rat#subp_activation_record;


    status.normal := TRUE;

    IF activation_lists_p = NIL THEN
      { No activation list has been initialized.  An activation list is initialized by
      { creating an activation list header for the installation control file used to
      { install the subproduct.

      NEXT current_activation_list_p IN activation_lists_segment_ptr.sequence_pointer;
      IF current_activation_list_p = NIL THEN
        osp$set_status_abnormal ('RA', rae$accessed_beyond_segment_eoi, 'ACTIVATION LIST', status);
        osp$append_status_parameter (osc$status_parameter_delimiter, 'ACTIVATION LIST HEADER', status);
        RETURN;
      IFEND;

      activation_lists_p := current_activation_list_p;

      current_activation_list_p^.installation_identifier := installation_identifier;
      current_activation_list_p^.number_of_subproducts := 0;
      current_activation_list_p^.first_subproduct_p := NIL;
      current_activation_list_p^.last_subproduct_p := NIL;
      current_activation_list_p^.next_activation_list_p := NIL;

    ELSE {there is at least one existing activation list}
      { Locate the activation list for the installation control file used to
      { install the deferred subproduct.

      current_activation_list_p := activation_lists_p;
      WHILE (installation_identifier <> current_activation_list_p^.installation_identifier) AND
            (current_activation_list_p^.next_activation_list_p <> NIL) DO
        current_activation_list_p := current_activation_list_p^.next_activation_list_p;
      WHILEND;

      IF installation_identifier <> current_activation_list_p^.installation_identifier THEN
        { The installation control file used to install the subproduct does not have
        { an activation list.  A new activation list is initialized and linked to
        { the last activation list.

        NEXT current_activation_list_p^.next_activation_list_p IN
              activation_lists_segment_ptr.sequence_pointer;
        IF current_activation_list_p = NIL THEN
          osp$set_status_abnormal ('RA', rae$accessed_beyond_segment_eoi, 'ACTIVATION LIST', status);
          osp$append_status_parameter (osc$status_parameter_delimiter, 'ACTIVATION LIST HEADER', status);
          RETURN;
        IFEND;

        current_activation_list_p := current_activation_list_p^.next_activation_list_p;

        current_activation_list_p^.installation_identifier := installation_identifier;
        current_activation_list_p^.number_of_subproducts := 0;
        current_activation_list_p^.first_subproduct_p := NIL;
        current_activation_list_p^.last_subproduct_p := NIL;
        current_activation_list_p^.next_activation_list_p := NIL;

      IFEND;
    IFEND;

    { Add the subproduct name to end of the current activation list.

    NEXT new_subproduct_activation_rec_p IN activation_lists_segment_ptr.sequence_pointer;
    IF new_subproduct_activation_rec_p = NIL THEN
      osp$set_status_abnormal ('RA', rae$accessed_beyond_segment_eoi, 'ACTIVATION LIST', status);
      osp$append_status_parameter (osc$status_parameter_delimiter, 'SUBPRODUCT ACTIVATION RECORD', status);
      RETURN;
    IFEND;

    IF current_activation_list_p^.first_subproduct_p = NIL THEN
      { This is the first name recorded.

      current_activation_list_p^.first_subproduct_p := new_subproduct_activation_rec_p;
    ELSE
      current_activation_list_p^.last_subproduct_p^.next_subproduct_p := new_subproduct_activation_rec_p;
    IFEND;

    current_activation_list_p^.number_of_subproducts := current_activation_list_p^.number_of_subproducts + 1;
    current_activation_list_p^.last_subproduct_p := new_subproduct_activation_rec_p;
    current_activation_list_p^.last_subproduct_p^.name := subproduct_name;
    current_activation_list_p^.last_subproduct_p^.processing_records_index := processing_records_index;
    current_activation_list_p^.last_subproduct_p^.next_subproduct_p := NIL;

  PROCEND add_to_activation_list;

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

{ PURPOSE:
{   This procedure establishes the job processing controls.
{
{ DESIGN:
{   This procedure prepares the job processing controls for product
{   activation.  The processing controls established here is on a smaller
{   scale then with a complete installation.  (Job processing records are not
{   required because there is no multi-job processing.  The medium
{   processing records are not needed because the loading has already
{   occurred.  And the job status record is created without using the
{   processing summary file.)
{
{   Display the installation identifier from the ICF to the job log.  This
{   was the identifier used to install the deferred subproducts from this
{   ICF.
{
{   Set the job identifier.
{
{   Set up the job processing status record.
{
{ NOTES:
{   This procedure is to provides symmetry with rap$install_product_command.
{

  PROCEDURE establish_processing_cntrls
    (    installation_identifier: rat$installation_identifier;
         activation_list_p: ^rat#activation_list_header;
     VAR installation_control_record {input, output} : rat$installation_control_record;
     VAR status: ost$status);


    VAR
      creation_date_time: ost$date_time,
      current_subproduct_p: ^rat#subp_activation_record,
      i: rat$subproduct_count,
      length: integer,
      line: string (osc$max_string_size);


    status.normal := TRUE;

    { Display installation identifier originally used to install the subproducts deferred to job log.

    STRINGREP (line, length, '  Activating subproducts previously deferred by installation ',
          installation_control_record.processing_header_p^.installation_identifier
          (1, clp$trimmed_string_size (installation_control_record.processing_header_p^.
          installation_identifier)), '.');

    pmp$log_ascii (line (1, length), $pmt$ascii_logset [pmc$job_log], pmc$msg_origin_program, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    { Set the installation identifier field in the installation control record.
    { The installation identifier will also be used as the job identifier.

    installation_control_record.processing_header_p^.installation_identifier := installation_identifier;
    installation_control_record.job_identifier := installation_identifier;

    { Clear the existing processing records' job identifier.

    FOR i := 1 TO UPPERBOUND (installation_control_record.subproduct_processing_records_p^) DO
      installation_control_record.subproduct_processing_records_p^ [i].job_identifier := osc$null_name;
    FOREND;

    { For each subproduct listed on the current ICF activation list, the
    { associated processing record's job identifier field is initialized to the
    { job identifier.

    current_subproduct_p := activation_list_p^.first_subproduct_p;
    WHILE current_subproduct_p <> NIL DO

      installation_control_record.subproduct_processing_records_p^
            [current_subproduct_p^.processing_records_index].job_identifier :=
            installation_control_record.job_identifier;

      current_subproduct_p := current_subproduct_p^.next_subproduct_p;
    WHILEND;

    { Set up the job status record.

    pmp$get_compact_date_time (creation_date_time, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    installation_control_record.job_status_record_p^.job_identifier :=
          installation_control_record.job_identifier;
    installation_control_record.job_status_record_p^.log_file_name := rac$command_log_name;
    installation_control_record.job_status_record_p^.date_time := creation_date_time;
    installation_control_record.job_status_record_p^.number_of_steps :=
          installation_control_record.processing_header_p^.number_of_steps;
    installation_control_record.job_status_record_p^.step_number := 0;
    installation_control_record.job_status_record_p^.step := rac$null_step;
    installation_control_record.job_status_record_p^.step_status := rac$step_started;
    installation_control_record.job_status_record_p^.initial_subproduct_count :=
          activation_list_p^.number_of_subproducts;
    installation_control_record.job_status_record_p^.started_subproduct_count := 0;
    installation_control_record.job_status_record_p^.completed_subproduct_count := 0;

  PROCEND establish_processing_cntrls;

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

{ PURPOSE:
{   This procedure establishes the set of activation tasks to be performed
{   on the selected products.  These tasks are recorded in the subproduct
{   processing record for those subproducts chosen to be activated.
{
{ DESIGN:
{   The subproduct tasks performed to activate are:  activate files, execute
{   installer proc, update directory and delete previous cycles (when save
{   previous cycles is true).
{
{ NOTES:
{   The usage of the processing record index is based on two asumptions.
{   First, that the packing list index (from which it originated) is
{   compatible, and second, the ICF cannot be replaced by the user.
{

  PROCEDURE establish_task_set
    (    activation_list_p: ^rat#activation_list_header;
         installation_control_record {input, output} : rat$installation_control_record);


    VAR
      activation_tasks: rat$task_selections,
      current_subproduct_p: ^rat#subp_activation_record,
      i: rat$subproduct_count;


    { Determine the installation step and task sets.

    IF installation_control_record.processing_header_p^.save_previous_cycles THEN
      installation_control_record.processing_header_p^.number_of_steps := 4;
      installation_control_record.processing_header_p^.step_set :=
            $rat$step_selections [rac$reconcile_subproducts_step, rac$activate_subproducts_step,
            rac$execute_installer_proc_step, rac$update_directory_step];
      activation_tasks := $rat$task_selections [rac$reconcile_file_cycles_task, rac$activate_files_task,
            rac$execute_installer_proc_task, rac$update_directory_task];
    ELSE {delete previous cycles}
      installation_control_record.processing_header_p^.number_of_steps := 5;
      installation_control_record.processing_header_p^.step_set :=
            $rat$step_selections [rac$reconcile_subproducts_step, rac$activate_subproducts_step,
            rac$execute_installer_proc_step, rac$update_directory_step, rac$delete_previous_cycles_step];
      activation_tasks := $rat$task_selections [rac$reconcile_file_cycles_task, rac$activate_files_task,
            rac$execute_installer_proc_task, rac$update_directory_task, rac$delete_previous_cycles_task];
    IFEND;

    { Clear the existing processing records' task sets.

    FOR i := 1 TO UPPERBOUND (installation_control_record.subproduct_processing_records_p^) DO
      installation_control_record.subproduct_processing_records_p^ [i].task_set := $rat$task_selections [];
      installation_control_record.subproduct_processing_records_p^ [i].task_status := rac$task_started;
    FOREND;

    { For each subproduct listed on the current activation list, the
    { associated processing record's task set field is initialized to the
    { activation tasks.

    current_subproduct_p := activation_list_p^.first_subproduct_p;
    WHILE current_subproduct_p <> NIL DO

      installation_control_record.subproduct_processing_records_p^
            [current_subproduct_p^.processing_records_index].task_set := activation_tasks;
      installation_control_record.subproduct_processing_records_p^
            [current_subproduct_p^.processing_records_index].task_status := rac$task_started;

      current_subproduct_p := current_subproduct_p^.next_subproduct_p;
    WHILEND;

  PROCEND establish_task_set;

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

{ PURPOSE:
{   This procedure validates the input subproduct list specified on
{   the activate product command call against the IDB Directory and returns
{   the activation lists.
{
{ DESIGN:
{   The IDB directory is opened for read access.  The directory determines
{   if the subproducts associated with the input list are deferred and able
{   to be activated.
{
{   The subproduct list is checked for keyword ALL.  Error is returned if
{   names are specified with keyword ALL.  The appropriate processing
{   procedure is then called.
{
{ NOTES:
{

  PROCEDURE get_activation_lists
    (    installation_database: rat$path;
         subproduct_list_p: ^clt$data_value;
     VAR activation_lists_segment_ptr {input, output} : amt$segment_pointer;
     VAR activation_lists_p: ^rat#activation_list_header;
     VAR status: ost$status);


    VAR
      current_p: ^clt$data_value,
      directory_fid: amt$file_identifier,
      directory_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;
    activation_lists_p := NIL;

    osp$establish_block_exit_hndlr (^abort_handler);

  /main/
    BEGIN

      rap$access_directory_for_read (installation_database, directory_pointers, directory_fid,
            directory_opened, status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;

      IF directory_pointers.header_p^.deferred_count = 0 THEN
        osp$set_status_abnormal ('RA', rae$no_deferred_subproducts, '', status);
        EXIT /main/;
      IFEND;

      mmp$create_scratch_segment (amc$sequence_pointer, mmc$as_random, activation_lists_segment_ptr, status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;

      RESET activation_lists_segment_ptr.sequence_pointer;

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

      IF subproduct_list_p^.kind = clc$keyword THEN

        process_product_list_all (directory_pointers, activation_lists_segment_ptr, activation_lists_p,
              status);

      ELSE {list of names specified}

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

        current_p := subproduct_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 (subproduct_list_p, directory_pointers, activation_lists_segment_ptr,
              activation_lists_p, status);

      IFEND;

    END /main/;

    IF directory_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 get_activation_lists;

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

{ PURPOSE:
{   This procedure builds the activation lists for all deferred subproducts.
{
{ DESIGN:
{   This procedure loops though the directory and adds every deferred
{   subproduct to the activation list that corresponds to that subproduct's
{   ICF.
{
{ NOTES:
{

  PROCEDURE process_product_list_all
    (    directory_pointers: rat$idb_directory_pointers;
     VAR activation_lists_segment_ptr {input, output} : amt$segment_pointer;
     VAR activation_lists_p: ^rat#activation_list_header;
     VAR status: ost$status);


    VAR
      i: rat$subproduct_count;


    status.normal := TRUE;

    FOR i := 1 TO UPPERBOUND (directory_pointers.directory_p^) DO
      IF directory_pointers.directory_p^ [i].deferred_information.installation_identifier <>
            rac$not_installed THEN

        { The subproduct is deferred.

        add_to_activation_list (directory_pointers.directory_p^ [i].subproduct,
              directory_pointers.directory_p^ [i].deferred_information.installation_identifier,
              directory_pointers.directory_p^ [i].deferred_information.packing_list_index,
              activation_lists_segment_ptr, activation_lists_p, status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;
      IFEND;
    FOREND;

  PROCEND process_product_list_all;

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

{ PURPOSE:
{   This procedure builds the activation lists for the deferred subproducts
{   specified from the subproduct list.
{
{ DESIGN:
{   This procedure loops though the subproduct list and attempts to locate
{   each product name in the directory.  If found and deferred, add
{   subproduct to the activation list that corresponds to it's ICF.  If not,
{   set validation error but continue processing.  A validation error causes
{   processing to stop.
{
{ NOTES:
{

  PROCEDURE process_product_list_names
    (    subproduct_list_p: ^clt$data_value;
         directory_pointers: rat$idb_directory_pointers;
     VAR activation_lists_segment_ptr {input, output} : amt$segment_pointer;
     VAR activation_lists_p: ^rat#activation_list_header;
     VAR status: ost$status);


    VAR
      current_subproduct_list_p: ^clt$data_value,
      i: rat$subproduct_count,
      ignore_status: ost$status,
      local_status: ost$status,
      subproduct_in_directory: boolean,
      validation_errors_occurred: boolean;


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

    current_subproduct_list_p := subproduct_list_p;
    WHILE current_subproduct_list_p <> NIL DO
      subproduct_in_directory := FALSE;

      FOR i := 1 TO UPPERBOUND (directory_pointers.directory_p^) DO

        IF current_subproduct_list_p^.element_value^.name_value =
              directory_pointers.directory_p^ [i].subproduct THEN
          { Subroduct name matches subproduct name for subproduct in directory.

          IF directory_pointers.directory_p^ [i].deferred_information.installation_identifier <>
                rac$not_installed THEN
            { This subproduct is deferred.

            add_to_activation_list (directory_pointers.directory_p^ [i].subproduct,
                  directory_pointers.directory_p^ [i].deferred_information.installation_identifier,
                  directory_pointers.directory_p^ [i].deferred_information.packing_list_index,
                  activation_lists_segment_ptr, activation_lists_p, status);
            IF NOT status.normal THEN
              RETURN;
            IFEND;
          ELSE {subproduct is not deferred}
            osp$set_status_abnormal ('RA', rae$subproduct_not_deferred,
                  directory_pointers.directory_p^ [i].subproduct
                  (1, clp$trimmed_string_size (directory_pointers.directory_p^ [i].subproduct)),
                  local_status);
            osp$append_status_parameter (osc$status_parameter_delimiter,
                  directory_pointers.directory_p^ [i].licensed_product
                  (1, clp$trimmed_string_size (directory_pointers.directory_p^ [i].licensed_product)),
                  local_status);
            osp$generate_log_message ($pmt$ascii_logset [pmc$job_log], local_status, ignore_status);
            validation_errors_occurred := TRUE;
          IFEND;

          subproduct_in_directory := TRUE;
        IFEND;
      FOREND;
      IF NOT subproduct_in_directory THEN
        osp$set_status_abnormal ('RA', rae$subproduct_not_in_directory,
              current_subproduct_list_p^.element_value^.name_value (1,
              clp$trimmed_string_size (current_subproduct_list_p^.element_value^.name_value)), local_status);
        osp$generate_log_message ($pmt$ascii_logset [pmc$job_log], local_status, ignore_status);
        validation_errors_occurred := TRUE;
      IFEND;
      current_subproduct_list_p := current_subproduct_list_p^.link;
    WHILEND;

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

  PROCEND process_product_list_names;
MODEND ram$activate_product_command;

