?? RIGHT := 110 ??
?? NEWTITLE := 'INSTALL_SOFTWARE Utility: DISPLAY_INSTALLED_SOFTWARE Subcommand.' ??
MODULE ram$display_installed_software;

{ PURPOSE:
{   This module contains the interface that displays information about
{   the currently active and/or deferred software.
{
{ DESIGN:
{   The compiled module resides in RAF$LIBRARY.
{
{ NOTES:
{

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc ost$name
*copyc ost$status
*copyc pmt$condition
*copyc rac$idb_directory_name
*copyc rac$not_installed
*copyc rac$packing_list_level
*copyc rae$install_software_cc
*copyc rat$idb_directory_types
?? POP ??
*copyc amp$get_segment_pointer
*copyc clp$build_path_subtitle
*copyc clp$build_standard_title
*copyc clp$close_display
*copyc clp$convert_integer_to_real
*copyc clp$convert_integer_to_rjstring
*copyc clp$convert_integer_to_string
*copyc clp$convert_real_to_string
*copyc clp$evaluate_parameters
*copyc clp$get_path_name
*copyc clp$horizontal_tab_display
*copyc clp$new_display_line
*copyc clp$open_display_reference
*copyc clp$put_display
*copyc clp$put_partial_display
*copyc clp$reset_for_next_display_page
*copyc clp$right_justify_string
*copyc clp$trimmed_string_size
*copyc clv$display_variables
*copyc clv$nil_display_control
*copyc fsp$close_file
*copyc fsp$open_file
*copyc osp$append_status_file
*copyc osp$disestablish_cond_handler
*copyc osp$establish_block_exit_hndlr
*copyc osp$generate_message
*copyc osp$set_status_abnormal
*copyc pmp$format_compact_date
*copyc pmp$format_compact_time
*copyc rap$access_directory_for_read
*copyc rav$installation_defaults

?? TITLE := 'Global Declarations Declared by This Module', EJECT ??

  { The following TYPE is used to contain the list of products specified
  { on the PRODUCT parameter of the DISPLAY_INSTALLED_SOFTWARE subcommand.
  { It is used during processing to allow the display of directory information
  { for the specified product(s).
  {
  { The record contains two fields.  The name field contains the product
  { name as specified by the user.  This name can either be a subproduct
  { or licensed product name.  Group names are not supported.  The boolean
  { is used during processing to indicate whether the product specified is
  { known to the directory, either as a subproduct or licensed product.

  TYPE
    rat#product_processing_type = record
      product_name: ost$name,
      known_to_directory: boolean,
    recend;

?? TITLE := '[XDCL] rap$display_installed_software', EJECT ??

{ PURPOSE:
{   This interface displays information about the currently active
{   and/or deferred products and subproducts using information in
{   the installation database directory.
{
{ DESIGN:
{
{ NOTES:
{

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


{ PROCEDURE disis_pdt (
{   product, products, p: any of
{       key
{         all
{       keyend
{       list of name
{     anyend = $optional
{   display_option, do: key
{       (brief, b)
{       (full, f)
{     keyend = brief
{   output, o: file = $output
{   display_hidden_values, dhv: (BY_NAME, HIDDEN) boolean = false
{   status)

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

  VAR
    pdt: [STATIC, READ, cls$declaration_section] record
      header: clt$pdt_header,
      names: array [1 .. 10] of clt$pdt_parameter_name,
      parameters: array [1 .. 5] of clt$pdt_parameter,
      type1: record
        header: clt$type_specification_header,
        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,
        qualifier: clt$keyword_type_qualifier,
        keyword_specs: array [1 .. 4] of clt$keyword_specification,
        default_value: string (5),
      recend,
      type3: record
        header: clt$type_specification_header,
        default_value: string (7),
      recend,
      type4: record
        header: clt$type_specification_header,
        default_value: string (5),
      recend,
      type5: record
        header: clt$type_specification_header,
      recend,
    recend := [
    [1,
    [89, 8, 17, 14, 3, 37, 915],
    clc$command, 10, 5, 0, 0, 1, 0, 5, ''], [
    ['DHV                            ',clc$abbreviation_entry, 4],
    ['DISPLAY_HIDDEN_VALUES          ',clc$nominal_entry, 4],
    ['DISPLAY_OPTION                 ',clc$nominal_entry, 2],
    ['DO                             ',clc$abbreviation_entry, 2],
    ['O                              ',clc$abbreviation_entry, 3],
    ['OUTPUT                         ',clc$nominal_entry, 3],
    ['P                              ',clc$abbreviation_entry, 1],
    ['PRODUCT                        ',clc$nominal_entry, 1],
    ['PRODUCTS                       ',clc$alias_entry, 1],
    ['STATUS                         ',clc$nominal_entry, 5]],
    [
{ PARAMETER 1
    [8, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation, clc$standard_parameter_checking, 85, clc$optional_parameter,
  0, 0],
{ PARAMETER 2
    [3, 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, 155,
  clc$optional_default_parameter, 0, 5],
{ PARAMETER 3
    [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, 3,
  clc$optional_default_parameter, 0, 7],
{ PARAMETER 4
    [2, clc$hidden_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name],
    clc$pass_by_value, clc$immediate_evaluation, clc$standard_parameter_checking, 3,
  clc$optional_default_parameter, 0, 5],
{ PARAMETER 5
    [10, 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$keyword_type], [4], [
    ['B                              ', clc$abbreviation_entry, clc$normal_usage_entry, 1],
    ['BRIEF                          ', clc$nominal_entry, clc$normal_usage_entry, 1],
    ['F                              ', clc$abbreviation_entry, clc$normal_usage_entry, 2],
    ['FULL                           ', clc$nominal_entry, clc$normal_usage_entry, 2]]
    ,
    'brief'],
{ PARAMETER 3
    [[1, 0, clc$file_type],
    '$output'],
{ PARAMETER 4
    [[1, 0, clc$boolean_type],
    'false'],
{ PARAMETER 5
    [[1, 0, clc$status_type]]];

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

    CONST
      p$product = 1,
      p$display_option = 2,
      p$output = 3,
      p$display_hidden_values = 4,
      p$status = 5;

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



    VAR
      default_ring_attributes: amt$ring_attributes,
      directory_pointers: rat$idb_directory_pointers,
      display_control: clt$display_control,
      display_opened: boolean,
      display_status: ost$status,
      length: integer,
      local_status: ost$status,
      idb_fid: amt$file_identifier,
      idb_opened: boolean;

?? TITLE := 'put_subtitle', EJECT ??

    PROCEDURE [INLINE] put_subtitle
      (VAR display_control: clt$display_control;
       VAR status: ost$status);

{ Add subtitles here, if needed. }

    PROCEND put_subtitle;
?? NEWTITLE := 'abort_handler', EJECT ??

{ PURPOSE:
{   This procedure cleans up when an abort situation occurs
{   within the block structure.
{
{ DESIGN:
{   If the files have been opened, they will be closed before the
{   the procedure returns.
{
{ 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 display_opened THEN
        clp$close_display (display_control, ignore_status);
      IFEND;

      IF idb_opened THEN
        fsp$close_file (idb_fid, ignore_status)
      IFEND

    PROCEND abort_handler;
*copyc clp$new_page_procedure
?? OLDTITLE, EJECT ??

    status.normal := TRUE;
    display_opened := FALSE;
    idb_opened := FALSE;
    display_control := clv$nil_display_control;
    #SPOIL (display_control);
    clv$titles_built := FALSE;
    clv$command_name := 'display_installed_software';

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

    default_ring_attributes.r1 := #RING (^default_ring_attributes);
    default_ring_attributes.r2 := #RING (^default_ring_attributes);
    default_ring_attributes.r3 := #RING (^default_ring_attributes);

    osp$establish_block_exit_hndlr (^abort_handler);

    display_opened := TRUE;
    clp$open_display_reference (pvt [p$output].value^.file_value^, ^clp$new_page_procedure, fsc$list,
          default_ring_attributes, display_control, status);
    IF NOT status.normal THEN
      display_opened := FALSE;
      RETURN;
    IFEND;

  /main/
    BEGIN

      rap$access_directory_for_read (rav$installation_defaults.installation_database, directory_pointers,
            idb_fid, idb_opened, status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;

      display_idb_information (directory_pointers.directory_p, pvt [p$product].value,
            pvt [p$display_option].value^.name_value, pvt [p$display_hidden_values].
            value^.boolean_value.value, display_control, status);
      IF NOT status.normal THEN
        EXIT /main/;
      IFEND;


    END /main/;

    IF display_opened THEN
      clp$close_display (display_control, display_status);
    IFEND;

    IF idb_opened THEN
      fsp$close_file (idb_fid, local_status);
    IFEND;

    IF status.normal AND (NOT local_status.normal) THEN
      status := local_status;
    ELSEIF status.normal AND (NOT display_status.normal) THEN
      status := display_status;
    IFEND;

    osp$disestablish_cond_handler;

  PROCEND rap$display_installed_software;
?? TITLE := 'analyze_product_list', EJECT ??

{ PURPOSE:
{   This procedure analyzes the optional product list and returns
{   a boolean indicating whether or not the keyword  ALL was
{   specified, or a list of products was specified.  Also returned
{   is a processing array of records containing information about
{   each product specified on the product parameter.
{
{ DESIGN:
{   If the keyword ALL is specified, or the name ALL is used along with
{   a list of product names, the return boolean parameter is set FALSE to
{   indicate that ALL was specified.  A TRUE value signifies that a list
{   of products was specified.
{
{ NOTES:
{   If the user specified a list of names, including the keyword ALL, this
{   procedure returns an abnormal status and sets the boolean to FALSE.
{
{   The test for whether or not the product parameter was specified was done
{   in the calling procedure.
{
{   For ease of coding and in order to incorporate an additional field for
{   each product specified, the product list specified by the user is transferred
{   into an array.  This array is used later in processing instead of the input
{   list because of the need to indicate whether or not a given product is
{   known to the directory.  This knowledge is stored in the array.
{

  PROCEDURE analyze_product_list
    (    product_input_p: ^clt$data_value;
     VAR product_processing_p: ^array [ * ] of rat#product_processing_type;
     VAR user_specified_product_list: boolean;
     VAR status: ost$status);


    VAR
      current_p: ^clt$data_value,
      i: rat$subproduct_count,
      subproduct_in_directory: boolean;


    user_specified_product_list := TRUE;

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

    IF product_input_p^.kind = clc$keyword THEN

      user_specified_product_list := FALSE;

    ELSE {list of names specified}

      current_p := product_input_p;
      i := 0;
      WHILE current_p <> NIL DO

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

        IF current_p^.element_value^.name_value = 'ALL' THEN
          osp$set_status_abnormal ('RA', rae$specified_names_and_key_all, '', status);
          user_specified_product_list := FALSE;
          RETURN;
        IFEND;

        { Transfer list of names to array of names to be returned.

        i := i + 1;
        product_processing_p^ [i].product_name := current_p^.element_value^.name_value;
        product_processing_p^ [i].known_to_directory := FALSE;
        current_p := current_p^.link;

      WHILEND;

    IFEND;

  PROCEND analyze_product_list;
?? TITLE := 'display_information_record', EJECT ??

{ PURPOSE:
{   This procedure displays information about the subproducts
{   that have been installed on a mainframe.
{
{ DESIGN:
{   Information from the installation data base is displayed.
{
{ NOTES:
{
{

  PROCEDURE display_information_record
    (    information_record: rat$information_record;
         display_option: ost$name;
         display_hidden_values: boolean;
     VAR display_control: clt$display_control;
     VAR status: ost$status);

    VAR
      date: ost$date,
      display_status: ost$status,
      time: ost$time;


    status.normal := TRUE;
    display_status.normal := TRUE;

    write_strings ('    Level:                    ', information_record.subproduct_level, FALSE,
          display_control, display_status);

    IF (display_hidden_values) AND (information_record.internal_level <> osc$null_name) THEN
      write_strings ('    Internal Level:           ', information_record.internal_level, FALSE,
            display_control, display_status);
    IFEND;

    pmp$format_compact_date (information_record.date_installed, osc$iso_date, date, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    pmp$format_compact_time (information_record.date_installed, osc$hms_time, time, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    write_strings ('    Install Date/Time:        ', date.iso, TRUE, display_control, display_status);
    write_strings (' ', time.hms, FALSE, display_control, display_status);

    IF (display_option = 'FULL') THEN

      write_strings ('    Packing List:             ', information_record.packing_list, FALSE,
            display_control, display_status);
      write_strings ('    Installation Id:          ', information_record.installation_identifier, FALSE,
            display_control, display_status);

    IFEND;

    IF display_hidden_values THEN
      write_strings ('    SIF Identifier:           ', information_record.sif_identifier, FALSE,
            display_control, display_status);
    IFEND;

    IF status.normal AND (NOT display_status.normal) THEN
      status := display_status;
    IFEND;

  PROCEND display_information_record;

?? TITLE := 'display_idb_information', EJECT ??

{ PURPOSE:
{   This procedure displays information about the subproducts
{   that have been installed on a mainframe.
{
{ DESIGN:
{   Information from the installation data base is displayed.  If the product
{   parameter was specified on the call, information about only the specified
{   list of subproducts and/or licensed products will be displayed.
{
{ NOTES:
{   This code assumes that if the active directory indicates not installed
{   that the subproduct (deferred and correction base) are not installed and
{   thus issues a message to that effect.  This will have to be dealt with
{   in BCU processing.

  PROCEDURE display_idb_information
    (    directory_p: ^rat$directory;
         product_input_p: ^clt$data_value;
         display_option: ost$name;
         display_hidden_values: boolean;
     VAR display_control: clt$display_control;
     VAR status: ost$status);

?? TITLE := 'size_of_list_parameter', EJECT ??

    FUNCTION size_of_list_parameter
      (    product_input_p: ^clt$data_value): rat$subproduct_count;

      VAR
        current_p: ^clt$data_value,
        size: rat$subproduct_count;

      { This fuction determines the number of elements in a list parameter.
      { Since a list parameter can also assume another type (eg. keyword), it
      { is important to check that the type is list before proceeding.  If the
      { parameter is a not a list type, return with a zero value.

      size := 0;

      IF product_input_p^.kind <> clc$list THEN
        size_of_list_parameter := 0;
        RETURN;
      IFEND;

      current_p := product_input_p;
      WHILE current_p <> NIL DO
        size := size + 1;
        current_p := current_p^.link;
      WHILEND;

      size_of_list_parameter := size;

    FUNCEND size_of_list_parameter;

?? OLDTITLE, EJECT ??


    VAR
      bottom_margin: amt$page_length,
      display_status: ost$status,
      i: rat$subproduct_count,
      ignore_status: ost$status,
      last_licensed_product: rat$licensed_product,
      local_status: ost$status,
      product_in_user_list: boolean,
      product_processing_p: ^array [ * ] of rat#product_processing_type,
      subproducts_displayed: boolean,
      user_specified_product_list: boolean;

    status.normal := TRUE;
    bottom_margin := 9;

    display_status.normal := TRUE;
    last_licensed_product := '';
    subproducts_displayed:= FALSE;

    {  If the product parameter was specified, create memory for product processing
    {  array, and search the optional product list for the keyword ALL or ALL specified
    {  as a name.  If ALL is specified, return FALSE in user_specified_product_list,
    {  else, return a TRUE indicating that a specified list of names should
    {  be displayed by this procedure.  Also, return an array of product names
    {  specified by user on the product parameter.

    IF product_input_p <> NIL THEN
      PUSH product_processing_p: [1 .. size_of_list_parameter (product_input_p)];
      analyze_product_list (product_input_p, product_processing_p, user_specified_product_list, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;
    ELSE
      user_specified_product_list := FALSE;
    IFEND;

  /display/
    FOR i := 1 TO UPPERBOUND (directory_p^) DO

      IF user_specified_product_list THEN

        { Find and display those products specified by user in product parameter.

        find_product_in_user_list (directory_p^ [i], product_processing_p, product_in_user_list, status);
        IF NOT product_in_user_list THEN
          CYCLE /display/; { Current directory subproduct not in user product list. }
        IFEND;

      IFEND;

      { If processing drops through to here, it means that either all subproducts are
      { are being processed, or that a subproduct in the list was found in the directory
      { and will now be displayed.

      subproducts_displayed := TRUE;

      IF display_control.line_number > display_control.page_length - bottom_margin THEN
        display_control.line_number := display_control.page_length;
      IFEND;

      IF directory_p^ [i].licensed_product <> last_licensed_product THEN

        last_licensed_product := directory_p^ [i].licensed_product;
        write_strings ('', '', FALSE, display_control, display_status);
        write_strings ('Licensed Product ', directory_p^ [i].
              licensed_product (1, clp$trimmed_string_size (directory_p^ [i].licensed_product)),
              TRUE, display_control, display_status);
        write_strings (':', '', FALSE, display_control, display_status);

      IFEND;

      write_strings ('', '', FALSE, display_control, display_status);

      write_strings ('  Subproduct ', directory_p^ [i].subproduct
            (1, clp$trimmed_string_size (directory_p^ [i].subproduct)), TRUE, display_control,
            display_status);
      write_strings (' Information:', '', FALSE, display_control, display_status);
      write_strings ('', '', FALSE, display_control, display_status);

      IF (display_option = 'FULL') THEN
        write_strings ('    Description: ', directory_p^ [i].description, FALSE, display_control,
              display_status);
        write_strings ('', '', FALSE, display_control, display_status);
      IFEND;

      IF directory_p^ [i].subproduct_corrupted = TRUE THEN
        write_strings ('    *** ACTIVE LEVEL WAS CORRUPTED BY A FAILED INSTALLATION ATTEMPT.', '',
              FALSE, display_control, display_status);
        write_strings ('    *** SEE LOGS FROM INSTALLATION IDENTIFIER ',
              directory_p^ [i].active_information.installation_identifier (1,
              clp$trimmed_string_size (directory_p^ [i].active_information.installation_identifier)),
              TRUE, display_control, display_status);
        write_strings ('.', '', FALSE, display_control, display_status);
      IFEND;

      IF directory_p^ [i].active_information.installation_identifier <> rac$not_installed THEN
        write_strings ('    Type:                     ACTIVE', '', FALSE, display_control, display_status);
        display_information_record (directory_p^ [i].active_information, display_option,
              display_hidden_values, display_control, status);
      ELSE
        write_strings ('    {Currently not installed}', '', FALSE, display_control, display_status);
      IFEND;
      IF directory_p^ [i].deferred_information.installation_identifier <> rac$not_installed THEN
        write_strings ('    Type:                     DEFERRED', '', FALSE, display_control, display_status);
        display_information_record (directory_p^ [i].deferred_information, display_option,
              display_hidden_values, display_control, status);
      IFEND;

      { Only show corrective base information if it is 1) present and 2) different from the active level.

      IF (directory_p^ [i].corrective_base_information.installation_identifier <> rac$not_installed) AND
            (directory_p^ [i].corrective_base_information.subproduct_level <>
            directory_p^ [i].active_information.subproduct_level) THEN
        write_strings ('    Type:                     CORRECTION BASE', '', FALSE, display_control,
              display_status);
        display_information_record (directory_p^ [i].corrective_base_information, display_option,
              display_hidden_values, display_control, status);
      IFEND;

    FOREND /display/;

    IF user_specified_product_list THEN

      write_strings ('', '', FALSE, display_control, display_status);

      IF NOT subproducts_displayed THEN
        osp$set_status_abnormal ('RA', rae$no_products_found, '', local_status);
        osp$generate_message (local_status, ignore_status);

      ELSE { one or more subproducts were displayed. }

        { If the user specified the product list parameter, display an error message for any
        { product that was not known to the directory, and therefore was not displayed.

        FOR i := 1 to UPPERBOUND (product_processing_p^) DO
          IF NOT product_processing_p^ [i].known_to_directory THEN
            osp$set_status_abnormal ('RA', rae$product_not_in_directory,
                 product_processing_p^ [i].product_name, local_status);
            osp$generate_message (local_status, ignore_status);
          IFEND;
        FOREND;

      IFEND;

    IFEND;

    IF status.normal AND (NOT display_status.normal) THEN
      status := display_status;
    IFEND;

  PROCEND display_idb_information;
?? TITLE := 'find_product_in_user_list', EJECT ??

{ PURPOSE:
{   This procedure returns a boolean value indicating whether a
{   subproduct is present in the product list specified by the user.
{   A flag is set in the product processing array indicating that
{   the product is known to the directory.
{
{ DESIGN:
{   A linear search is performed on the list of product names specified
{   by the user for either the licensed product or subproduct name from
{   the directory record.
{
{ NOTES:
{   Since the product list is expected to be very small (ie. < 25 products),
{   a linear search is performed.
{
{   The search to determine if a product is known to the directory is done
{   only on the subproduct and licensed product names.  The group name is
{   not supported, since group information is not stored in the directory.
{

  PROCEDURE find_product_in_user_list
    (    directory_record: rat$directory_record;
     VAR product_processing_p: ^array [ * ] of rat#product_processing_type;
     VAR product_in_user_list: boolean;
     VAR status: ost$status);

    VAR
      i: rat$subproduct_count;

    product_in_user_list := FALSE;
    FOR i := 1 to UPPERBOUND (product_processing_p^) DO

      IF (product_processing_p^ [i].product_name = directory_record.subproduct) OR
         (product_processing_p^ [i].product_name = directory_record.licensed_product) THEN
        product_in_user_list := TRUE;
        product_processing_p^ [i].known_to_directory := TRUE;
        RETURN;
      IFEND;

    FOREND;

PROCEND find_product_in_user_list;
?? TITLE := 'write_string_and_integer', EJECT ??

{ PURPOSE:
{   This procedure writes a string and a integer to the output display.
{
{ DESIGN:
{   The string and the integer are combined into one string and a procedure
{   writes them to output.
{
{ NOTES:
{
{

  PROCEDURE write_string_and_integer
    (    string_a: string ( * );
         integer_a: integer;
         continue_line: boolean;
     VAR display_control: clt$display_control;
     VAR display_status: ost$status);

    VAR
      ignore_status: ost$status,
      line_a: string (2 * fsc$max_path_size),
      line_b: string (2 * fsc$max_path_size),
      line_size_a: integer,
      line_size_b: integer;

    IF NOT display_status.normal THEN
      RETURN;
    IFEND;

    line_a := '';
    STRINGREP (line_a, line_size_a, integer_a);

    line_b := '';
    STRINGREP (line_b, line_size_b, string_a, line_a (2, line_size_a - 1));

    IF continue_line THEN
      clp$put_partial_display (display_control, line_b (1, line_size_b), clc$no_trim, amc$continue,
            ignore_status);
    ELSE
      clp$put_partial_display (display_control, line_b (1, line_size_b), clc$no_trim, amc$terminate,
            ignore_status);
    IFEND;

  PROCEND write_string_and_integer;
?? TITLE := 'write_strings', EJECT ??

{ PURPOSE:
{   This procedure writes two strings to the output display.
{
{ DESIGN:
{   The two strings are combined into one string and a procedure
{   writes them to output.
{
{ NOTES:
{
{

  PROCEDURE write_strings
    (    string_a: string ( * );
         string_b: string ( * );
         continue_line: boolean;
     VAR display_control: clt$display_control;
     VAR display_status: ost$status);


    VAR
      ignore_status: ost$status,
      line: string (osc$max_string_size),
      line_size: integer;


    IF NOT display_status.normal THEN
      RETURN;
    IFEND;

    line := '';
    STRINGREP (line, line_size, string_a, string_b);

    IF continue_line THEN
      clp$put_partial_display (display_control, line (1, line_size), clc$no_trim, amc$continue,
            ignore_status);
    ELSE
      clp$put_partial_display (display_control, line (1, line_size), clc$no_trim, amc$terminate,
            ignore_status);
    IFEND;

  PROCEND write_strings;

MODEND ram$display_installed_software;
