?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Operator Facility : Operator Menu Management' ??
MODULE ofm$operator_action_menu_r1;

{ PURPOSE:
{   This module contains the procedures to directly manipulate the list
{   of operator action menus for all tasks in the system.
{
{ DESIGN:
{   This is a system core module since the menu list is shared by all jobs
{   and must be interlocked at times. Each of its procedures locks the menu
{   list and then operates upon it by adding/deleting/modifying/obtaining
{   information from an entry as appropriate. The list is then unlocked
{   upon exit from the procedure.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc jmt$user_supplied_name
*copyc ofd$type_definition
*copyc ofe$error_codes
*copyc oft$menu_id
*copyc oft$operator_classes
*copyc oft$operator_menu_descriptor
*copyc ost$signature_lock
*copyc ost$status
?? POP ??
*copyc osp$clear_mainframe_sig_lock
*copyc osp$set_mainframe_sig_lock
*copyc osp$set_status_abnormal
*copyc osp$system_error
*copyc pmp$get_executing_task_gtid
*copyc pmp$get_job_names
*copyc pmp$ready_task
*copyc osv$mainframe_pageable_heap
?? TITLE := 'Global Declarations Declared by This Module', EJECT ??

  VAR
    ofv$menu_list_lock: [oss$mainframe_pageable, STATIC] ost$signature_lock := [0],
    ofv$menu_list_p: [oss$mainframe_pageable, STATIC] ^oft$operator_menu_descriptor := NIL;

?? TITLE := 'ofp$add_operator_menu', EJECT ??

*copyc ofh$add_operator_menu

  PROCEDURE [XDCL, #GATE] ofp$add_operator_menu
    (    menu_selections_p: ^oft$menu_selections;
         number_of_displayable_lines: oft$number_of_displayable_lines,
         number_of_choices: oft$number_of_choices;
         operator_class: oft$operator_class;
     VAR menu_id: oft$menu_id;
     VAR status: ost$status);

    VAR
      global_task_id: ost$global_task_id,
      menu_descriptor_p: ^oft$operator_menu_descriptor,
      menu_p: ^oft$operator_menu_descriptor,
      next_menu_id: [oss$mainframe_pageable, STATIC] oft$menu_id := 0,
      system_name: jmt$system_supplied_name,
      user_name: jmt$user_supplied_name;

    status.normal := TRUE;
    pmp$get_job_names (user_name, system_name, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    pmp$get_executing_task_gtid (global_task_id);

{ Allocate space for the new menu, and fill in those fields in the descriptor
{ which can be stored without protection from the menu locking mechanism.

    ALLOCATE menu_p IN osv$mainframe_pageable_heap^;
    menu_p^.source_task := global_task_id;
    menu_p^.job_name := system_name;
    menu_p^.menu_class := operator_class;
    menu_p^.number_of_displayable_lines := number_of_displayable_lines;
    menu_p^.number_of_choices := number_of_choices;
    menu_p^.choice_made := FALSE;
    menu_p^.menu_text := menu_selections_p^;
    menu_p^.help_text_p := NIL;
    menu_p^.help_text_line_count := 0;

    osp$set_mainframe_sig_lock (ofv$menu_list_lock);

{ Calculate a menu identifier for the new menu, and ensure that it is unique.

    menu_descriptor_p := ofv$menu_list_p;
    next_menu_id := (next_menu_id + 1) MOD ofc$max_menu_id;
    WHILE menu_descriptor_p <> NIL DO
      IF menu_descriptor_p^.menu_id <> next_menu_id THEN
        menu_descriptor_p := menu_descriptor_p^.next_descriptor_p;
      ELSE

{ The calculated menu_id is not unique. Generate a new menu_id and redo the
{ check for uniqueness.

        next_menu_id := (next_menu_id + 1) MOD ofc$max_menu_id;
        menu_descriptor_p := ofv$menu_list_p;
      IFEND;
    WHILEND;

{ Link the new menu into the list of menus, and store the fields in the
{ descriptor which require protection from the locking mechanism. The menu
{ list is a push down stack linked to the front.

    menu_p^.menu_id := next_menu_id;
    menu_p^.next_descriptor_p := ofv$menu_list_p;
    ofv$menu_list_p := menu_p;
    menu_id := menu_p^.menu_id;

    osp$clear_mainframe_sig_lock (ofv$menu_list_lock);

  PROCEND ofp$add_operator_menu;
?? TITLE := 'ofp$delete_operator_menu', EJECT ??

*copyc ofh$delete_operator_menu

  PROCEDURE [XDCL, #GATE] ofp$delete_operator_menu
    (    menu_id: oft$menu_id;
     VAR status: ost$status);

    VAR
      global_task_id: ost$global_task_id,
      menu_p: ^oft$operator_menu_descriptor,
      menu_previous_p: ^oft$operator_menu_descriptor;

    status.normal := TRUE;
    pmp$get_executing_task_gtid (global_task_id);
    osp$set_mainframe_sig_lock (ofv$menu_list_lock);

    menu_p := ofv$menu_list_p;
    menu_previous_p := NIL;

  /find_matching_menu_item/
    WHILE menu_p <> NIL DO
      IF (menu_p^.menu_id = menu_id) AND (menu_p^.source_task = global_task_id) THEN
        IF menu_previous_p = NIL THEN
          ofv$menu_list_p := menu_p^.next_descriptor_p;
        ELSE
          menu_previous_p^.next_descriptor_p := menu_p^.next_descriptor_p;
        IFEND;
        EXIT /find_matching_menu_item/;
      IFEND;

      menu_previous_p := menu_p;
      menu_p := menu_p^.next_descriptor_p;
    WHILEND /find_matching_menu_item/;

    osp$clear_mainframe_sig_lock (ofv$menu_list_lock);

    IF menu_p = NIL THEN
      osp$set_status_abnormal (ofc$operator_facility_id, ofe$invalid_menu_id, ' ', status);
    ELSE
      FREE menu_p IN osv$mainframe_pageable_heap^;
    IFEND;

  PROCEND ofp$delete_operator_menu;
?? TITLE := 'ofp$get_first_operator_menu_r1', EJECT ??

*copyc ofh$get_first_operator_menu_r1

  PROCEDURE [XDCL, #GATE] ofp$get_first_operator_menu_r1
    (    active_operator_classes: oft$operator_classes;
     VAR menu_descriptor: oft$operator_menu_descriptor;
     VAR status: ost$status);

    VAR
      menu_p: ^oft$operator_menu_descriptor;

    status.normal := TRUE;
    IF ofv$menu_list_p = NIL THEN
      osp$set_status_abnormal (ofc$operator_facility_id, ofe$no_menus_available, ' ', status);
      RETURN;
    IFEND;

    osp$set_mainframe_sig_lock (ofv$menu_list_lock);
    menu_p := ofv$menu_list_p;

  /find_first_available_menu/
    WHILE menu_p <> NIL DO
      IF ((menu_p^.menu_class IN active_operator_classes) AND NOT (menu_p^.choice_made)) THEN
        menu_descriptor := menu_p^;
        EXIT /find_first_available_menu/;
      IFEND;

      menu_p := menu_p^.next_descriptor_p;
    WHILEND /find_first_available_menu/;

    osp$clear_mainframe_sig_lock (ofv$menu_list_lock);
    IF menu_p = NIL THEN
      osp$set_status_abnormal (ofc$operator_facility_id, ofe$no_menus_available, ' ', status);
    IFEND;

  PROCEND ofp$get_first_operator_menu_r1;
?? TITLE := 'ofp$get_menu_choice', EJECT ??

*copyc ofh$get_menu_choice

  PROCEDURE [XDCL, #GATE] ofp$get_menu_choice
    (    menu_id: oft$menu_id;
     VAR choice_made: boolean;
     VAR choice: oft$number_of_choices;
     VAR response_string: ost$string;
     VAR status: ost$status);

    VAR
      global_task_id: ost$global_task_id,
      menu_p: ^oft$operator_menu_descriptor,
      menu_previous_p: ^oft$operator_menu_descriptor;

    status.normal := TRUE;
    pmp$get_executing_task_gtid (global_task_id);
    osp$set_mainframe_sig_lock (ofv$menu_list_lock);

    menu_p := ofv$menu_list_p;
    menu_previous_p := NIL;

  /find_matching_menu_item/
    WHILE menu_p <> NIL DO
      IF (menu_p^.menu_id = menu_id) AND (menu_p^.source_task = global_task_id) THEN
        choice_made := menu_p^.choice_made;
        IF NOT choice_made THEN
          EXIT /find_matching_menu_item/;
        IFEND;

        choice := menu_p^.choice;
        response_string := menu_p^.response_string;
        IF response_string.value (1,1) = '?' THEN

{ Help was requested so the menu descriptor should be kept around but left in a state that allows another
{ choice to be made.

          menu_p^.choice_made := FALSE;
          menu_p^.response_string.value (1, *) := ' ';
        ELSE { help was not requested so menu can be deleted }
          IF menu_previous_p = NIL THEN
            ofv$menu_list_p := menu_p^.next_descriptor_p;
          ELSE
            menu_previous_p^.next_descriptor_p := menu_p^.next_descriptor_p;
          IFEND;
        IFEND;
        EXIT /find_matching_menu_item/;
      IFEND;

      menu_previous_p := menu_p;
      menu_p := menu_p^.next_descriptor_p;
    WHILEND /find_matching_menu_item/;

    osp$clear_mainframe_sig_lock (ofv$menu_list_lock);

    IF menu_p = NIL THEN
      osp$set_status_abnormal (ofc$operator_facility_id, ofe$invalid_menu_id, ' ', status);
    ELSEIF menu_p^.choice_made THEN
      FREE menu_p IN osv$mainframe_pageable_heap^;
    IFEND;

  PROCEND ofp$get_menu_choice;
?? TITLE := 'ofp$get_menu_help_text_r1', EJECT ??

*copyc ofh$get_menu_help_text_r1

  PROCEDURE [XDCL, #GATE] ofp$get_menu_help_text_r1
    (    menu_id: oft$menu_id;
         global_task_id: ost$global_task_id;
         help_text_p: {input, output} ^oft$menu_selections;
     VAR help_text_found: boolean;
     VAR help_text_line_count: oft$number_of_displayable_lines;
     VAR status: ost$status);

    VAR
      menu_p: ^oft$operator_menu_descriptor;

    status.normal := TRUE;
    IF ofv$menu_list_p = NIL THEN
      osp$set_status_abnormal (ofc$operator_facility_id, ofe$invalid_menu_id, ' ', status);
      RETURN;
    IFEND;

    osp$set_mainframe_sig_lock (ofv$menu_list_lock);

    help_text_found := FALSE;
    menu_p := ofv$menu_list_p;

  /find_matching_menu_item/
    BEGIN
      WHILE menu_p <> NIL DO
        IF (menu_p^.menu_id = menu_id) AND (menu_p^.source_task = global_task_id) THEN
          IF menu_p^.help_text_p <> NIL THEN
            help_text_found := TRUE;
            help_text_p^ := menu_p^.help_text_p^;
            help_text_line_count := menu_p^.help_text_line_count;
          IFEND;
          EXIT /find_matching_menu_item/;
        ELSE
          menu_p := menu_p^.next_descriptor_p;
        IFEND;
      WHILEND;

      osp$set_status_abnormal (ofc$operator_facility_id, ofe$invalid_menu_id, ' ', status);
    END /find_matching_menu_item/;

    osp$clear_mainframe_sig_lock (ofv$menu_list_lock);

  PROCEND ofp$get_menu_help_text_r1;
?? TITLE := 'ofp$get_next_operator_menu_r1', EJECT ??

*copyc ofh$get_next_operator_menu_r1

  PROCEDURE [XDCL, #GATE] ofp$get_next_operator_menu_r1
    (    current_menu_id: oft$menu_id;
         active_operator_classes: oft$operator_classes;
     VAR menu_descriptor: oft$operator_menu_descriptor;
     VAR status: ost$status);

    VAR
      menu_current_p: ^oft$operator_menu_descriptor,
      menu_next_p: ^oft$operator_menu_descriptor;

    status.normal := TRUE;
    IF ofv$menu_list_p = NIL THEN
      osp$set_status_abnormal (ofc$operator_facility_id, ofe$no_menus_available, ' ', status);
      RETURN;
    IFEND;

    osp$set_mainframe_sig_lock (ofv$menu_list_lock);

  /get_next_available_menu/
    BEGIN
      menu_current_p := ofv$menu_list_p;

    /search_for_current/
      WHILE menu_current_p <> NIL DO
        IF menu_current_p^.menu_id = current_menu_id THEN
          EXIT /search_for_current/;
        IFEND;
        menu_current_p := menu_current_p^.next_descriptor_p;
      WHILEND /search_for_current/;

{ If the current menu specified by the caller was found, then begin the search
{ for the next available menu from that point. If the current menu was not
{ located, then assume that another operator has responded to the "missing"
{ menu, and search the list of menus for the first one the caller is validated
{ to display.

      IF menu_current_p <> NIL THEN
        menu_next_p := menu_current_p^.next_descriptor_p;
      ELSE
        menu_next_p := ofv$menu_list_p;
        WHILE menu_next_p <> NIL DO
          IF ((menu_next_p^.menu_class IN active_operator_classes) AND NOT (menu_next_p^.choice_made)) THEN
            menu_descriptor := menu_next_p^;
            EXIT /get_next_available_menu/;
          IFEND;

          menu_next_p := menu_next_p^.next_descriptor_p;
        WHILEND;

        osp$set_status_abnormal (ofc$operator_facility_id, ofe$no_menus_available, ' ', status);
        EXIT /get_next_available_menu/;
      IFEND;

      WHILE TRUE DO
        IF menu_next_p = NIL THEN
          menu_next_p := ofv$menu_list_p;
        IFEND;

        IF menu_next_p = menu_current_p THEN

{ If the current menu is the only one in the menu list which the caller
{ may display and a choice has been made for that menu, then return an
{ error status indicating that no menus are available. This situation
{ can arise if the caller has successfully made a choice for the menu
{ and then attempts to get the next available one before the task which
{ sent the original menu gets the CPU back and acts to have it removed
{ from the list.

          IF menu_next_p^.choice_made THEN
            osp$set_status_abnormal (ofc$operator_facility_id, ofe$no_menus_available, ' ', status);
          ELSE
            osp$set_status_abnormal (ofc$operator_facility_id, ofe$one_menu_available, ' ', status);
          IFEND;

          EXIT /get_next_available_menu/;
        IFEND;

        IF ((menu_next_p^.menu_class IN active_operator_classes) AND NOT (menu_next_p^.choice_made)) THEN
          menu_descriptor := menu_next_p^;
          EXIT /get_next_available_menu/;
        IFEND;

        menu_next_p := menu_next_p^.next_descriptor_p;
      WHILEND;
    END /get_next_available_menu/;

    osp$clear_mainframe_sig_lock (ofv$menu_list_lock);

  PROCEND ofp$get_next_operator_menu_r1;
?? TITLE := 'ofp$job_operator_menus_active', EJECT ??

*copyc ofh$job_operator_menus_active

  FUNCTION [XDCL, UNSAFE] ofp$job_operator_menus_active
    (    job_name: jmt$system_supplied_name): boolean;

    VAR
      menu_p: ^oft$operator_menu_descriptor;

    ofp$job_operator_menus_active := FALSE;
    IF ofv$menu_list_p = NIL THEN
      RETURN;
    IFEND;

    osp$set_mainframe_sig_lock (ofv$menu_list_lock);
    menu_p := ofv$menu_list_p;

  /search_for_active_menu/
    WHILE menu_p <> NIL DO
      IF ((menu_p^.job_name = job_name) AND NOT (menu_p^.choice_made)) THEN
        ofp$job_operator_menus_active := TRUE;
        EXIT /search_for_active_menu/;
      IFEND;

      menu_p := menu_p^.next_descriptor_p;
    WHILEND /search_for_active_menu/;

    osp$clear_mainframe_sig_lock (ofv$menu_list_lock);

  FUNCEND ofp$job_operator_menus_active;
?? TITLE := 'ofp$store_menu_choice_r1', EJECT ??

*copyc ofh$store_menu_choice_r1

  PROCEDURE [XDCL, #GATE] ofp$store_menu_choice_r1
    (    menu_id: oft$menu_id;
         active_operator_classes: oft$operator_classes;
         choice: oft$number_of_choices;
         response_string: ost$string;
     VAR status: ost$status);

    VAR
      menu_p: ^oft$operator_menu_descriptor;

    status.normal := TRUE;
    IF ofv$menu_list_p = NIL THEN
      osp$set_status_abnormal (ofc$operator_facility_id, ofe$invalid_menu_id, ' ', status);
      RETURN;
    IFEND;

    osp$set_mainframe_sig_lock (ofv$menu_list_lock);

    menu_p := ofv$menu_list_p;

  /find_matching_menu_item/
    BEGIN
      WHILE menu_p <> NIL DO
        IF ((menu_p^.menu_id = menu_id) AND NOT menu_p^.choice_made) THEN
          IF menu_p^.menu_class IN active_operator_classes THEN
            menu_p^.choice := choice;
            menu_p^.response_string := response_string;
            menu_p^.choice_made := TRUE;
            pmp$ready_task (menu_p^.source_task, status);
            EXIT /find_matching_menu_item/;
          ELSE
            osp$set_status_abnormal (ofc$operator_facility_id, ofe$invalid_menu_id, ' ', status);
            EXIT /find_matching_menu_item/;
          IFEND;
        ELSE
          menu_p := menu_p^.next_descriptor_p;
        IFEND;
      WHILEND;

      osp$set_status_abnormal (ofc$operator_facility_id, ofe$invalid_menu_id, ' ', status);
      EXIT /find_matching_menu_item/;
    END /find_matching_menu_item/;

    osp$clear_mainframe_sig_lock (ofv$menu_list_lock);

  PROCEND ofp$store_menu_choice_r1;
?? TITLE := 'ofp$store_menu_help_text', EJECT ??

*copyc ofh$store_menu_help_text

  PROCEDURE [XDCL, #GATE] ofp$store_menu_help_text
    (    menu_id: oft$menu_id;
         help_text: oft$menu_selections;
         help_text_line_count: oft$number_of_displayable_lines;
     VAR status: ost$status);

    CONST
      no_help_text = ' No help is available';

    VAR
      global_task_id: ost$global_task_id,
      menu_p: ^oft$operator_menu_descriptor;

    status.normal := TRUE;
    IF ofv$menu_list_p = NIL THEN
      osp$set_status_abnormal (ofc$operator_facility_id, ofe$invalid_menu_id, ' ', status);
      RETURN;
    IFEND;

    pmp$get_executing_task_gtid (global_task_id);
    osp$set_mainframe_sig_lock (ofv$menu_list_lock);

    menu_p := ofv$menu_list_p;

  /find_matching_menu_item/
    BEGIN
      WHILE menu_p <> NIL DO
        IF (menu_p^.menu_id = menu_id) AND (menu_p^.source_task = global_task_id) THEN
          ALLOCATE menu_p^.help_text_p IN osv$mainframe_pageable_heap^;
          menu_p^.help_text_p^ := help_text;
          IF help_text_line_count = 0 THEN
            menu_p^.help_text_p^ [1] := no_help_text;
            menu_p^.help_text_line_count := 1;
          ELSE
            menu_p^.help_text_line_count := help_text_line_count;
          IFEND;
          EXIT /find_matching_menu_item/;
        ELSE
          menu_p := menu_p^.next_descriptor_p;
        IFEND;
      WHILEND;

      osp$set_status_abnormal (ofc$operator_facility_id, ofe$invalid_menu_id, ' ', status);
    END /find_matching_menu_item/;

    osp$clear_mainframe_sig_lock (ofv$menu_list_lock);

  PROCEND ofp$store_menu_help_text;
MODEND ofm$operator_action_menu_r1;
