?? RIGHT := 110 ??
?? NEWTITLE := 'INSTALL_SOFTWARE Utility: CHANGE_INSTALLATION_PATH Subcommand.' ??
MODULE ram$change_installation_path;

{ PURPOSE:
{   This module contains the interface that allows the user to
{   define a product's installation path.
{
{ DESIGN:
{   The compiled module resides in RAF$LIBRARY.
{
{ NOTES:
{

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc rac$packing_list_level
*copyc rac$pacs_processor_version
*copyc rac$special_product_designators
*copyc rae$install_software_cc
*copyc rat$processing_types
?? POP ??
*copyc amp$fetch_access_information
*copyc amp$get_segment_pointer
*copyc clp$evaluate_parameters
*copyc clp$trimmed_string_size
*copyc fsp$close_file
*copyc fsp$open_file
*copyc mmp$create_scratch_segment
*copyc mmp$delete_scratch_segment
*copyc osp$append_status_file
*copyc osp$append_status_parameter
*copyc osp$disestablish_cond_handler
*copyc osp$establish_block_exit_hndlr
*copyc osp$generate_log_message
*copyc osp$set_status_abnormal
*copyc rav$installation_defaults
*copyc rav$product_reference
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

  TYPE
    subproduct_pointers_array = array [ * ] of rat$subproduct_info_pointers;

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

{ PURPOSE:
{   This interface allows users to define the family and/or user
{   name portion of a product's installation path, when the
{   path is declared as user definable.
{
{ DESIGN:
{
{ NOTES:
{

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


{ PROCEDURE chaip_pdt (
{   packing_list, pl: name = $required
{   product, p: name = $required
{   family_name, fn: name = $optional
{   user_name, un: name = $optional
{   status)

?? PUSH (LISTEXT := ON) ??

  VAR
    pdt: [STATIC, READ, cls$declaration_section] record
      header: clt$pdt_header,
      names: array [1 .. 9] of clt$pdt_parameter_name,
      parameters: array [1 .. 5] of clt$pdt_parameter,
      type1: record
        header: clt$type_specification_header,
        qualifier: clt$name_type_qualifier,
      recend,
      type2: record
        header: clt$type_specification_header,
        qualifier: clt$name_type_qualifier,
      recend,
      type3: record
        header: clt$type_specification_header,
        qualifier: clt$name_type_qualifier,
      recend,
      type4: record
        header: clt$type_specification_header,
        qualifier: clt$name_type_qualifier,
      recend,
      type5: record
        header: clt$type_specification_header,
      recend,
    recend := [
    [1,
    [88, 9, 21, 9, 34, 28, 765],
    clc$command, 9, 5, 2, 0, 0, 0, 5, 'CHAIP_PDT'], [
    ['FAMILY_NAME                    ',clc$nominal_entry, 3],
    ['FN                             ',clc$abbreviation_entry, 3],
    ['P                              ',clc$abbreviation_entry, 2],
    ['PACKING_LIST                   ',clc$nominal_entry, 1],
    ['PL                             ',clc$abbreviation_entry, 1],
    ['PRODUCT                        ',clc$nominal_entry, 2],
    ['STATUS                         ',clc$nominal_entry, 5],
    ['UN                             ',clc$abbreviation_entry, 4],
    ['USER_NAME                      ',clc$nominal_entry, 4]],
    [
{ PARAMETER 1
    [4, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation, clc$standard_parameter_checking, 5, clc$required_parameter, 0
  , 0],
{ PARAMETER 2
    [6, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation, clc$standard_parameter_checking, 5, clc$required_parameter, 0
  , 0],
{ PARAMETER 3
    [1, 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, 5, clc$optional_parameter, 0
  , 0],
{ PARAMETER 4
    [9, 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, 5, clc$optional_parameter, 0
  , 0],
{ PARAMETER 5
    [7, 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$name_type], [1, osc$max_name_size]],
{ PARAMETER 2
    [[1, 0, clc$name_type], [1, osc$max_name_size]],
{ PARAMETER 3
    [[1, 0, clc$name_type], [1, osc$max_name_size]],
{ PARAMETER 4
    [[1, 0, clc$name_type], [1, osc$max_name_size]],
{ PARAMETER 5
    [[1, 0, clc$status_type]]];

?? POP ??

  CONST
    p$packing_list = 1,
    p$product = 2,
    p$family_name = 3,
    p$user_name = 4,
    p$status = 5;

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


    VAR
      file_opened: boolean,
      ignore_status: ost$status,
      local_status: ost$status,
      packing_list_fid: amt$file_identifier,
      packing_list_pointers: rat$packing_list_pointers,
      packing_list_segment_pointer: amt$segment_pointer,
      scratch_segment_pointer: amt$segment_pointer,
      sequence_length: integer,
      subproduct_pointers_p: ^subproduct_pointers_array;


?? 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 scratch
{   sequence and packing list 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 scratch_segment_pointer.sequence_pointer <> NIL THEN
        mmp$delete_scratch_segment (scratch_segment_pointer, ignore_status);
        scratch_segment_pointer.sequence_pointer := NIL;
      IFEND;

      fsp$close_file (packing_list_fid, ignore_status);

    PROCEND abort_handler;

?? OLDTITLE, EJECT ??


    status.normal := TRUE;

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

    IF (NOT pvt [p$family_name].specified) AND (NOT pvt [p$user_name].specified) THEN
      osp$set_status_abnormal ('RA', rae$family_or_user_param_reqrd, '', status);
      RETURN;
    IFEND;

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

    osp$establish_block_exit_hndlr (^abort_handler);

  /main/
    BEGIN

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

      open_packing_list_write_access (pvt [p$packing_list].value^.name_value,
            rav$installation_defaults.installation_database, scratch_segment_pointer, packing_list_fid,
            file_opened, packing_list_segment_pointer, sequence_length, status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;

      establish_packing_list_pointers (pvt [p$packing_list].value^.name_value,
            rav$installation_defaults.installation_database, scratch_segment_pointer, packing_list_pointers,
            status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;

      PUSH subproduct_pointers_p: [1 .. packing_list_pointers.header_p^.subproduct_count];

      establish_subproduct_pointers (pvt [p$packing_list].value^.name_value,
            rav$installation_defaults.installation_database, packing_list_pointers, subproduct_pointers_p,
            status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;

      process_change_request (pvt [p$product].value^.name_value, pvt [p$family_name], pvt [p$user_name],
            subproduct_pointers_p, status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;

      write_packing_list_file (pvt [p$packing_list].value^.name_value,
            rav$installation_defaults.installation_database, sequence_length, scratch_segment_pointer,
            packing_list_segment_pointer, status);

    END /main/;

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

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

    osp$disestablish_cond_handler;

  PROCEND rap$change_installation_path;

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

{ PURPOSE:
{   This procedure ...
{
{ DESIGN:
{
{ NOTES:
{

  PROCEDURE change_path
    (    family_name: clt$parameter_value;
         user_name: clt$parameter_value;
         subproduct_pointers {output} : rat$subproduct_info_pointers;
     VAR status: ost$status);


    VAR
      family_catalog_index: rat$path_container_index,
      user_catalog_index: rat$path_container_index;


    status.normal := TRUE;

    IF subproduct_pointers.attributes_p^.installation_path_option = rac$not_definable THEN

      osp$set_status_abnormal ('RA', rae$inst_path_not_definable, subproduct_pointers.attributes_p^.name,
            status);

    ELSEIF (subproduct_pointers.attributes_p^.installation_path_option = rac$definable_user_name) AND
          family_name.specified THEN

      osp$set_status_abnormal ('RA', rae$sip_family_not_definable, subproduct_pointers.attributes_p^.name,
            status);

    ELSEIF (subproduct_pointers.attributes_p^.installation_path_option = rac$definable_family_name) AND
          user_name.specified THEN

      osp$set_status_abnormal ('RA', rae$sip_user_not_definable, subproduct_pointers.attributes_p^.name,
            status);

    ELSE {the change is valid}

      family_catalog_index := subproduct_pointers.attributes_p^.installation_path.path_container_index;

      IF family_name.specified THEN
        subproduct_pointers.path_container_p^ [family_catalog_index] := family_name.value^.name_value;
      IFEND;

      IF user_name.specified THEN
        user_catalog_index := family_catalog_index + 1;
        subproduct_pointers.path_container_p^ [user_catalog_index] := user_name.value^.name_value;
      IFEND;

    IFEND;

  PROCEND change_path;

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

{ PURPOSE:
{   This procedure establishes the pointers to the major data structures
{   in the packing list.
{
{ DESIGN:
{
{   Validation errors are returned in the status variable.
{
{ NOTES:
{

  PROCEDURE establish_packing_list_pointers
    (    packing_list_name: ost$name;
         installation_database: rat$path;
         scratch_segment_pointer: amt$segment_pointer;
     VAR packing_list_pointers: rat$packing_list_pointers;
     VAR status: ost$status);


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


    status.normal := TRUE;

    packing_list_pointers.sequence_p := scratch_segment_pointer.sequence_pointer;

    RESET packing_list_pointers.sequence_p;
    NEXT packing_list_pointers.sequence_descriptor_p IN packing_list_pointers.sequence_p;
    IF packing_list_pointers.sequence_descriptor_p = NIL THEN
      osp$set_status_abnormal ('RA', rae$unexpected_eof_packing_list, packing_list_name, status);
      osp$append_status_file (osc$status_parameter_delimiter, installation_database.
            path (1, installation_database.size), status);
      RETURN;
    IFEND;

    IF packing_list_pointers.sequence_descriptor_p^.sequence_type <> rac$packing_list_sequence THEN
      osp$set_status_abnormal ('RA', rae$invalid_packing_list, packing_list_name, status);
      osp$append_status_file (osc$status_parameter_delimiter, installation_database.
            path (1, installation_database.size), status);
      RETURN;
    IFEND;

    IF packing_list_pointers.sequence_descriptor_p^.sequence_level <> rac$packing_list_level THEN
      osp$set_status_abnormal ('RA', rae$incompatible_sequence_level, 'PACKING LIST', status);
      osp$append_status_parameter (osc$status_parameter_delimiter,
            packing_list_pointers.sequence_descriptor_p^.sequence_level, status);
      RETURN;
    IFEND;

    IF packing_list_pointers.sequence_descriptor_p^.processor_version <> rac$pacs_processor_version THEN
      osp$set_status_abnormal ('RA', rae$different_processor_version, 'PACKING LIST', local_status);
      osp$generate_log_message ($pmt$ascii_logset [pmc$job_log], local_status, ignore_status);
    IFEND;

    NEXT packing_list_pointers.header_p IN packing_list_pointers.sequence_p;
    IF packing_list_pointers.header_p = NIL THEN
      osp$set_status_abnormal ('RA', rae$unexpected_eof_packing_list, packing_list_name, status);
      osp$append_status_file (osc$status_parameter_delimiter, installation_database.
            path (1, installation_database.size), status);
      RETURN;
    IFEND;

    packing_list_pointers.order_medium := packing_list_pointers.header_p^.order_medium;

    IF packing_list_pointers.order_medium = rac$tape THEN

      packing_list_pointers.tape_subproduct_indexer_p := #PTR (packing_list_pointers.header_p^.
            tape_subproduct_indexer_p, packing_list_pointers.sequence_p^);

      packing_list_pointers.tape_vsns_p := #PTR (packing_list_pointers.header_p^.tape_vsns_p,
            packing_list_pointers.sequence_p^);

    ELSE { order medium = rac$disk }

      packing_list_pointers.disk_subproduct_indexer_p := #PTR (packing_list_pointers.header_p^.
            disk_subproduct_indexer_p, packing_list_pointers.sequence_p^);

    IFEND;

  PROCEND establish_packing_list_pointers;

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

{ PURPOSE:
{   This procedure establishes the pointers to the major data structures
{   of each subproduct info sequence within the packing list.
{
{ DESIGN:
{
{ NOTES:
{

  PROCEDURE establish_subproduct_pointers
    (    packing_list_name: ost$name;
         installation_database: rat$path;
     VAR packing_list_pointers {input} : rat$packing_list_pointers;
     VAR subproduct_pointers_p: ^subproduct_pointers_array;
     VAR status: ost$status);


    VAR
      i: rat$subproduct_count,
      subproduct_pointers: rat$subproduct_info_pointers,
      subproduct_seq_length: amt$file_length,
      subproduct_seq_p: ^cell;


    status.normal := TRUE;

    FOR i := 1 TO packing_list_pointers.header_p^.subproduct_count DO

{ Locate the start of the subproduct info within the packing list.

      IF packing_list_pointers.order_medium = rac$tape THEN
        subproduct_seq_length := packing_list_pointers.tape_subproduct_indexer_p^ [i].subproduct_seq_length;
        subproduct_seq_p := #PTR (packing_list_pointers.tape_subproduct_indexer_p^ [i].subproduct_seq_p,
              packing_list_pointers.sequence_p^);
      ELSE {order medium = rac$disk}
        subproduct_seq_length := packing_list_pointers.disk_subproduct_indexer_p^ [i].subproduct_seq_length;
        subproduct_seq_p := #PTR (packing_list_pointers.disk_subproduct_indexer_p^ [i].subproduct_seq_p,
              packing_list_pointers.sequence_p^);
      IFEND;

      RESET packing_list_pointers.sequence_p TO subproduct_seq_p;

{ Establish the subproduct info as an accessable sequence.

      NEXT subproduct_pointers.subproduct_info_seq_p: [[REP subproduct_seq_length OF cell]] IN
            packing_list_pointers.sequence_p;
      IF subproduct_pointers.subproduct_info_seq_p = NIL THEN
        osp$set_status_abnormal ('RA', rae$unexpected_eof_packing_list, packing_list_name, status);
        osp$append_status_file (osc$status_parameter_delimiter, installation_database.
              path (1, installation_database.size), status);
        RETURN;
      IFEND;

      RESET packing_list_pointers.sequence_p TO subproduct_seq_p;

{ Establish pointers to the major conponents in the subproduct info sequence.

      NEXT subproduct_pointers.sequence_descriptor_p IN packing_list_pointers.sequence_p;
      IF subproduct_pointers.sequence_descriptor_p = NIL THEN
        osp$set_status_abnormal ('RA', rae$unexpected_eof_packing_list, packing_list_name, status);
        osp$append_status_file (osc$status_parameter_delimiter, installation_database.
              path (1, installation_database.size), status);
        RETURN;
      IFEND;

      NEXT subproduct_pointers.info_header_p IN packing_list_pointers.sequence_p;
      IF subproduct_pointers.info_header_p = NIL THEN
        osp$set_status_abnormal ('RA', rae$unexpected_eof_packing_list, packing_list_name, status);
        osp$append_status_file (osc$status_parameter_delimiter, installation_database.
              path (1, installation_database.size), status);
        RETURN;
      IFEND;

      subproduct_pointers.attributes_p := #PTR (subproduct_pointers.info_header_p^.attributes_p,
            subproduct_pointers.subproduct_info_seq_p^);

      subproduct_pointers.path_container_p := #PTR (subproduct_pointers.info_header_p^.path_container_p,
            subproduct_pointers.subproduct_info_seq_p^);

{ Assign the values for the subproduct info pointers just created to the installation control record.

      subproduct_pointers_p^ [i] := subproduct_pointers;

    FOREND;

  PROCEND establish_subproduct_pointers;

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

{ PURPOSE:
{   The purpose of this procedure is to open the packing_list for updating.
{
{ DESIGN:
{   This procedure opens the packing list file in write (modify) mode.  The
{   contents of the packing_list file is copied to a memory scratch
{   sequence where the changes are made.  When all changes are successfully
{   completed the packing list contents from memory is then copied back
{   over the packing list file.
{
{ NOTES:
{

  PROCEDURE open_packing_list_write_access
    (    packing_list_name: ost$name;
         installation_database: rat$path;
     VAR scratch_segment_pointer: amt$segment_pointer;
     VAR packing_list_fid: amt$file_identifier;
     VAR file_opened: boolean;
     VAR packing_list_segment_pointer: amt$segment_pointer;
     VAR sequence_length: integer;
     VAR status: ost$status);


    VAR
      access_information: array [1 .. 1] of amt$access_info,
      attachment_options: array [1 .. 2] of fst$attachment_option,
      file_seq_contents_p: ^SEQ ( * ),
      ignore_status: ost$status,
      memory_seq_contents_p: ^SEQ ( * ),
      packing_list: rat$path;


    status.normal := TRUE;

{ Assemble the path to the packing list using the installation database path and the packing list name.

    STRINGREP (packing_list.path, packing_list.size, rav$installation_defaults.installation_database.
          path (1, rav$installation_defaults.installation_database.size),
          '.', packing_list_name (1, clp$trimmed_string_size (packing_list_name)));

  /main/
    BEGIN

      attachment_options [1].selector := fsc$access_and_share_modes;
      attachment_options [1].access_modes.selector := fsc$specific_access_modes;
      attachment_options [1].access_modes.value := $fst$file_access_options [fsc$read, fsc$modify];
      attachment_options [1].share_modes.selector := fsc$specific_share_modes;
      attachment_options [1].share_modes.value := $fst$file_access_options [];
      attachment_options [2].selector := fsc$create_file;
      attachment_options [2].create_file := TRUE;

      file_opened := TRUE;
      fsp$open_file (packing_list.path (1, packing_list.size), amc$segment, ^attachment_options, NIL, NIL,
            NIL, NIL, packing_list_fid, status);
      IF NOT status.normal THEN
        file_opened := FALSE;
        EXIT /main/;
      IFEND;

      amp$get_segment_pointer (packing_list_fid, amc$sequence_pointer, packing_list_segment_pointer, status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;

{ Copy the packing_list into the scratch memory segment.

      access_information [1].key := amc$eoi_byte_address;
      amp$fetch_access_information (packing_list_fid, access_information, status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;
      sequence_length := access_information [1].eoi_byte_address;

      RESET packing_list_segment_pointer.sequence_pointer;
      NEXT file_seq_contents_p: [[REP sequence_length OF cell]] IN
            packing_list_segment_pointer.sequence_pointer;
      IF file_seq_contents_p = NIL THEN
        osp$set_status_abnormal ('RA', rae$unexpected_eof_packing_list,
              packing_list_name (1, clp$trimmed_string_size (packing_list_name)), status);
        osp$append_status_file (osc$status_parameter_delimiter, installation_database.
              path (1, installation_database.size), status);
        EXIT /main/;
      IFEND;

      RESET scratch_segment_pointer.sequence_pointer;
      NEXT memory_seq_contents_p: [[REP sequence_length OF cell]] IN scratch_segment_pointer.sequence_pointer;
      IF memory_seq_contents_p = NIL THEN
        osp$set_status_abnormal ('RA', rae$accessed_beyond_segment_eoi, 'packing list', status);
        osp$append_status_parameter (osc$status_parameter_delimiter, 'MEMORY SEQUENCE', status);
        EXIT /main/;
      IFEND;

      memory_seq_contents_p^ := file_seq_contents_p^;

    END /main/;

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

  PROCEND open_packing_list_write_access;

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

{ PURPOSE:
{   This procedure ...
{
{ DESIGN:
{
{ NOTES:
{

  PROCEDURE process_change_request
    (    product_name: ost$name;
         family_name: clt$parameter_value;
         user_name: clt$parameter_value;
     VAR subproduct_pointers_p {input} : ^subproduct_pointers_array;
     VAR status: ost$status);


    VAR
      group_name: ost$name,
      i: rat$subproduct_count,
      j: 0 .. rac$max_additional_products,
      local_status: ost$status,
      changes_made: boolean,
      product_reference: rat$product_references,
      subproduct_attributes_p: ^rat$subproduct_attributes;


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

    FOR i := 1 TO UPPERBOUND (subproduct_pointers_p^) DO
      subproduct_attributes_p := subproduct_pointers_p^ [i].attributes_p;

      IF product_name = subproduct_attributes_p^.licensed_product THEN

        product_reference := rac$licensed_product;
        change_path (family_name, user_name, subproduct_pointers_p^ [i], local_status);
        IF local_status.normal THEN
          changes_made := TRUE;
        IFEND;
      ELSEIF product_name = subproduct_attributes_p^.name THEN

        product_reference := rac$subproduct;
        change_path (family_name, user_name, subproduct_pointers_p^ [i], status);
        IF NOT status.normal THEN
          RETURN;
        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/
        FOR j := 1 TO UPPERBOUND (subproduct_attributes_p^.additional_products) DO
          IF group_name = subproduct_attributes_p^.additional_products [j] THEN
            product_reference := rac$group;
            change_path (family_name, user_name, subproduct_pointers_p^ [i], local_status);
            IF local_status.normal THEN
              changes_made := TRUE;
            IFEND;
            EXIT /group/;
          IFEND;
        FOREND /group/;

      IFEND;
    FOREND;

    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)), status);
    ELSEIF (product_reference <> rac$subproduct) AND (NOT changes_made) THEN
      osp$set_status_abnormal ('RA', rae$no_path_changes_made, rav$product_reference [product_reference],
            status);
      osp$append_status_parameter (osc$status_parameter_delimiter,
            product_name (1, clp$trimmed_string_size (product_name)), status);
    IFEND;

  PROCEND process_change_request;

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

{ PURPOSE:
{   This procedure copies the modified packing list from memory into the
{   packing list file.
{
{ DESIGN:
{
{ NOTES:
{

  PROCEDURE write_packing_list_file
    (    packing_list_name: ost$name;
         installation_database: rat$path;
         sequence_length: integer;
     VAR scratch_segment_pointer {input} : amt$segment_pointer;
     VAR packing_list_segment_pointer: amt$segment_pointer;
     VAR status: ost$status);


    VAR
      file_seq_contents_p: ^rat$packing_list_sequence,
      memory_seq_contents_p: ^rat$packing_list_sequence;


    status.normal := TRUE;

{ Copy the packing_list from the scratch memory segment to the packing list file.

    RESET scratch_segment_pointer.sequence_pointer;
    NEXT memory_seq_contents_p: [[REP sequence_length OF cell]] IN scratch_segment_pointer.sequence_pointer;
    IF memory_seq_contents_p = NIL THEN
      osp$set_status_abnormal ('RA', rae$accessed_beyond_segment_eoi, 'packing list', status);
      osp$append_status_parameter (osc$status_parameter_delimiter, 'MEMORY SEQUENCE', status);
      RETURN;
    IFEND;

    RESET packing_list_segment_pointer.sequence_pointer;
    NEXT file_seq_contents_p: [[REP sequence_length OF cell]] IN
          packing_list_segment_pointer.sequence_pointer;
    IF file_seq_contents_p = NIL THEN
      osp$set_status_abnormal ('RA', rae$unexpected_eof_packing_list,
            packing_list_name (1, clp$trimmed_string_size (packing_list_name)), status);
      osp$append_status_file (osc$status_parameter_delimiter, installation_database.
            path (1, installation_database.size), status);
      RETURN;
    IFEND;

    file_seq_contents_p^ := memory_seq_contents_p^;

  PROCEND write_packing_list_file;
MODEND ram$change_installation_path;
