?? NEWTITLE := 'NOS/VE Job Scheduling : convert_to_scheduler_types' ??
MODULE jmm$convert_to_scheduler_types;
?? RIGHT := 110 ??

{ PURPOSE:
{   This module provides the logic to translate scheduler tables to
{   profile objects and profile objects to scheduler tables.  This
{   is the only module that specifically manipulates scheduler tables
{   within the modules that make up MANAGE_ACTIVE_SCHEDULING and
{   ADMINISTER_SCHEDULING.
{
{ DESIGN:
{   Contains one routine to translate profile objects to scheduler tables
{   and one routine to translate scheduler tables to objects.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
*copyc jmc$attribute_keyword_offsets
*copyc jmc$profile_constants
*copyc jmt$application_index
*copyc jmt$application_attributes
*copyc jmt$application_table
*copyc jmt$dispatching_control
*copyc jmt$job_category_data
*copyc jmt$job_class_attributes
*copyc jmt$job_class_table
*copyc jmt$job_scheduler_table
*copyc jmt$output_class_attributes
*copyc jmt$profile_data
*copyc jmt$service_class_attributes
*copyc jmt$service_class_table
*copyc jmp$delete_attributes
*copyc jmp$get_application_record
*copyc jmp$get_attributes_for_display
*copyc jmp$get_category_data
*copyc jmp$get_defined_classes
*copyc jmp$get_job_class_record
*copyc jmp$get_scheduler_table
*copyc jmp$get_service_class_record
*copyc jmp$internal_error
*copyc pmp$convert_mainframe_to_binary
*copyc jmv$object_definition
*copyc jmv$object_heap
*copyc jmv$working_storage
?? TITLE := 'Global Declarations Declared by This Module', EJECT ??

  CONST
    microseconds_per_millisecond = 1000,
    milliseconds_per_second = 1000,
    microseconds_per_second = milliseconds_per_second * 1000;

  CONST
    dispatching_priority_offset = 1;

?? TITLE := ' build_shell', EJECT ??

{ PURPOSE:
{   Build an attribute shell so that the class attributes can be just stored
{   with appropriate assignment statements.
{
{ DESIGN:
{   Use the attribute description to create and empty shell of attributes of
{   the desired object type.
{
{ NOTES:
{   List attributes must be handled by the caller seperately or by calling
{   this routine to build just that part of the attribute structure when
{   the size of the list is known.

  PROCEDURE build_shell
    (    attribute_definition: jmt$profile_declaration;
         list_length: integer;
     VAR new_attribute: jmt$object_attribute);

    VAR
      i: integer;

    new_attribute.kind := attribute_definition.kind;
    CASE attribute_definition.kind OF
    = jmc$list, jmc$editable_list =
      IF list_length > 0 THEN
        ALLOCATE new_attribute.attribute_list: [1 .. list_length] IN jmv$object_heap^;
        IF new_attribute.attribute_list = NIL THEN
          jmp$internal_error (87);
        IFEND;
        FOR i := 1 TO list_length DO
          build_shell (attribute_definition.declarations^ [1]^, 0, new_attribute.attribute_list^ [i]);
        FOREND;
      ELSE
        new_attribute.kind := jmc$empty;
      IFEND;
    = jmc$type =
      ALLOCATE new_attribute.attribute_list: [1 .. attribute_definition.count] IN jmv$object_heap^;
      IF new_attribute.attribute_list = NIL THEN
        jmp$internal_error (88);
      IFEND;
      FOR i := 1 TO attribute_definition.count DO
        build_shell (attribute_definition.declarations^ [i]^, 0, new_attribute.attribute_list^ [i]);
      FOREND;
    = jmc$range =
      ALLOCATE new_attribute.attribute_list: [1 .. 2] IN jmv$object_heap^;
      IF new_attribute.attribute_list = NIL THEN
        jmp$internal_error (89);
      IFEND;
      FOR i := 1 TO 2 DO
        build_shell (attribute_definition.declarations^ [1]^, 0, new_attribute.attribute_list^ [i]);
      FOREND;
    = jmc$name =
      ALLOCATE new_attribute.name IN jmv$object_heap^;
      IF new_attribute.name = NIL THEN
        jmp$internal_error (90);
      IFEND;
    = jmc$file =
      new_attribute.kind := jmc$empty;
    ELSE
    CASEND;
  PROCEND build_shell;
?? TITLE := '[XDCL] jmp$build_category_object', EJECT ??

{ PURPOSE:
{   Builds an object containing the system category information.
{
{ DESIGN:
{   Fetch the job category data.  Create a dummy job category object and
{   into one statistic field place the data from the category sequence.
{   Into the other statistic place the data from the set array.

  PROCEDURE [XDCL] jmp$build_category_object
    (VAR profile: jmt$profile_data;
     VAR status: ost$status);

?? NEWTITLE := ' build_category_list', EJECT ??

{ PURPOSE:
{   Builds an attribute list of categories from a category set.
{
{ DESIGN:
{   Define a category object for each bit set in the category set.  If the
{   category name already exists then use the existing object in the list.
{   Build a list to pass back the list of category object references.

    PROCEDURE build_category_list
      (    categories: jmt$job_category_set;
       VAR attribute: jmt$object_attribute);

      VAR
        job_category: jmt$profile_object_reference,
        list_size: integer,
        list_index: integer;

      IF categories = $jmt$job_category_set [] THEN
        attribute.kind := jmc$none;
        RETURN; {----->
      IFEND;

      list_size := 0;
      job_category := profile.objects [jmc$profile_category];
      WHILE job_category <> NIL DO
        IF (job_category^.index - 1) IN categories THEN
          list_size := list_size + 1;
        IFEND;
        job_category := job_category^.next_object;
      WHILEND;

      attribute.kind := jmc$list;
      ALLOCATE attribute.attribute_list: [1 .. list_size] IN jmv$object_heap^;
      IF attribute.attribute_list = NIL THEN
        jmp$internal_error (91);
      IFEND;

      list_index := 0;
      job_category := profile.objects [jmc$profile_category];
      WHILE job_category <> NIL DO
        IF (job_category^.index - 1) IN categories THEN
          list_index := list_index + 1;
          attribute.attribute_list^ [list_index].kind := jmc$object;
          attribute.attribute_list^ [list_index].object_p := job_category;
          job_category^.references := job_category^.references + 1;
        IFEND;
        job_category := job_category^.next_object;
      WHILEND;
    PROCEND build_category_list;

?? OLDTITLE, EJECT ??

    VAR
      i: integer,
      s: ost$name,
      job_category: jmt$profile_object_reference,
      category_data: jmt$job_category_data,
      list_size: integer,
      list_index: integer,
      cik: jmt$job_category_item_kind,
      category_item: ^jmt$job_category_item,
      item_display: ^jmt$object_attribute_list,
      set_value_display: ^jmt$object_attribute_list,
      category_attributes: ^jmt$object_attribute_list,
      category_definition: jmt$profile_declaration,
      attribute: jmt$object_attribute;

    VAR
      convert: record
        case boolean of
        = TRUE =
          offset: 0 .. 0ffffffff(16),
        = FALSE =
          category_reference: jmt$job_category_reference,
        casend,
      recend;

    VAR
      item_name: [STATIC] array [jmt$job_category_item_kind] of ost$name := ['cpu_time_limit',
            'sru_time_limit', 'magnetic_tape_limit', 'working_set', 'login_account', 'login_project',
            'login_family', 'login_user', 'user_job_name', 'original_application_name', 'job_mode',
            'job_priority', 'job_qualifier', 'or_conditions'];

    jmp$get_category_data (category_data, jmv$working_storage, status);

    ALLOCATE job_category IN jmv$object_heap^;
    IF job_category = NIL THEN
      jmp$internal_error (92);
    IFEND;
    job_category^.name := 'Category_structure';
    job_category^.kind := jmc$profile_category;
    job_category^.index := 1;
    job_category^.definition_id := 'Category_structure';
    job_category^.references := 0;
    category_definition := jmv$object_definition [jmc$profile_category].declaration;
    build_shell (category_definition, 0, attribute);
    category_attributes := attribute.attribute_list;

    IF category_data.item_list <> NIL THEN
      list_size := #SIZE (category_data.item_list^) DIV #SIZE (jmt$job_category_item);
      RESET category_data.item_list;

      build_shell (category_definition.declarations^ [jmc$c_data_display]^,
            list_size, category_attributes^ [jmc$c_data_display]);

      FOR list_index := 1 TO list_size DO
        NEXT category_item IN category_data.item_list;
        item_display := category_attributes^ [jmc$c_data_display].attribute_list^ [list_index].attribute_list;

        convert.category_reference := category_item^.skip_item;
        item_display^ [1].number := convert.offset DIV #SIZE (jmt$job_category_item);

        convert.category_reference := category_item^.next_item;
        item_display^ [2].number := convert.offset DIV #SIZE (jmt$job_category_item);

        build_category_list (category_item^.categories, item_display^ [3]);
        item_display^ [4].name^ := item_name [category_item^.kind];
        CASE category_item^.kind OF
        = jmc$ca_cpu_time_limit, jmc$ca_sru_time_limit, jmc$ca_mag_tape_limit, jmc$ca_working_set =
          item_display^ [5].kind := jmc$number;
          item_display^ [5].number := category_item^.number;
        = jmc$ca_or_conditions =
          build_category_list (category_item^.members, item_display^ [5]);
        ELSE
          item_display^ [5].kind := jmc$name;
          ALLOCATE item_display^ [5].name IN jmv$object_heap^;
          IF item_display = NIL THEN
            jmp$internal_error (93);
          IFEND;
          item_display^ [5].name^ := category_item^.name;
        CASEND;
      FOREND;
    IFEND;

    list_size := $INTEGER (UPPERVALUE (cik)) + 1;
    build_shell (category_definition.declarations^ [jmc$c_set_display]^,
          list_size, category_attributes^ [jmc$c_set_display]);
    set_value_display := category_attributes^ [jmc$c_set_display].attribute_list;

    FOR cik := LOWERBOUND (category_data.initial_set_values)
          TO UPPERBOUND (category_data.initial_set_values) DO
      list_index := $INTEGER (cik) + 1;
      set_value_display^ [list_index].attribute_list^ [1].name^ := item_name [cik];
      build_category_list (category_data.initial_set_values [cik],
            set_value_display^ [list_index].attribute_list^ [2]);
    FOREND;

    job_category^.attributes := attribute;
    job_category^.next_object := profile.objects [jmc$profile_category];
    profile.objects [jmc$profile_category] := job_category;

    job_category := job_category^.next_object;
    WHILE job_category <> NIL DO
      s := job_category^.name;
      i := 31;
      WHILE s (i) = ' ' DO
        i := i - 1;
      WHILEND;
      STRINGREP (job_category^.name, i, s (1, i), job_category^.index);
      job_category := job_category^.next_object;
    WHILEND;

  PROCEND jmp$build_category_object;

?? TITLE := '[XDCL] jmp$build_profile_from_system', EJECT ??

{ PURPOSE:
{   Builds the objects and attributes necessary to describe the current
{   system profile.
{
{ DESIGN:
{   Obtain the list of job, service, application class names from the
{   system.  Build an object of the appropriate type for each name
{   obtained and then build the attributes for that class.
{
{ NOTES:
{   This routine should only be used to build a profile either to
{   verify the system tables or to construct something good enough to
{   be able to activate another profile.

  PROCEDURE [XDCL] jmp$build_profile_from_system
    (VAR profile: jmt$profile_data;
     VAR status: ost$status);

    VAR
      job_class_list: array [1 .. jmc$maximum_job_classes] of jmt$profile_object_reference,
      service_class_list: array [1 .. jmc$maximum_service_classes] of jmt$profile_object_reference,
      application_list: array [1 .. jmc$maximum_application_index] of jmt$profile_object_reference,
      job_category_list: array [1 .. jmc$maximum_job_categories + 1] of jmt$profile_object_reference;

    VAR
      cpu_quantum_time: ost$task_time_slice;

?? NEWTITLE := ' build_application_object', EJECT ??

{ PURPOSE:
{   Builds an application object with the specified name.
{
{ DESIGN:
{   Fetch the application record for the specified application name.  Set
{   the name, kind, and definition_id of the object.  Store the attributes
{   from the application record into the application attribute shell.

    PROCEDURE build_application_object
      (    application_name: jmt$application_name;
           application_index: integer;
       VAR application: jmt$profile_object;
       VAR status: ost$status);

      VAR
        application_table: jmt$application_attributes,
        application_attributes: ^jmt$object_attribute_list,
        service_class: jmt$profile_object_reference,
        attributes: jmt$object_attribute;

      jmp$get_application_record (application_name, application_table, status);

      application.kind := jmc$profile_application;
      application.name := application_table.name;
      application.changed := FALSE;
      application.definition_id := application_table.profile_identification;
      application.index := application_index;
      application.references := 0;
      build_shell (jmv$object_definition [application.kind].declaration, 0, attributes);
      application_attributes := attributes.attribute_list;

      application_attributes^ [jmc$ap_definition_name].name^ := application_table.profile_identification;

{   Group definition attributes

      application_attributes^ [jmc$ap_enable_application_sched].bool :=
            application_table.enable_application_scheduling;

      IF application_table.service_class_index = 0 THEN
        application_attributes^ [jmc$ap_service_class].kind := jmc$empty;
      ELSE
        service_class := service_class_list [application_table.service_class_index];
        application_attributes^ [jmc$ap_service_class].object_p := service_class;
        service_class^.references := service_class^.references + 1;
      IFEND;

{   Group control attributes

      application_attributes^ [jmc$ap_cyclic_aging_interval].number :=
            application_table.cyclic_aging_interval;
      application_attributes^ [jmc$ap_maximum_working_set].number := application_table.maximum_working_set;
      application_attributes^ [jmc$ap_minimum_working_set].number := application_table.minimum_working_set;
      application_attributes^ [jmc$ap_page_aging_interval].number := application_table.page_aging_interval;

      application.attributes := attributes;
    PROCEND build_application_object;
?? TITLE := ' build_category_list', EJECT ??

{ PURPOSE:
{   Builds a list of categories from a category set.
{
{ DESIGN:
{   Define a category object for each bit set in the category set.  If the
{   category name already exists then use the existing category object.
{   Build a list to pass back the category object references.

    PROCEDURE build_category_list
      (    categories: jmt$job_category_set;
       VAR attribute: jmt$object_attribute);

      VAR
        job_category_name: string (31),
        job_category: jmt$profile_object_reference,
        name_length: integer,
        list_size: integer,
        list_index: integer,
        category_index: integer;

      IF categories = $jmt$job_category_set [] THEN
        attribute.kind := jmc$none;
        RETURN; {----->
      IFEND;

      list_size := 0;
      FOR category_index := 1 TO UPPERBOUND (job_category_list) DO
        IF (category_index - 1) IN categories THEN
          list_size := list_size + 1;
          IF job_category_list [category_index] = NIL THEN
            ALLOCATE job_category IN jmv$object_heap^;
            IF job_category = NIL THEN
              jmp$internal_error (94);
            IFEND;
            STRINGREP (job_category_name, name_length, 'CATEGORY', category_index);
            job_category_name (9) := '_';
            job_category^.name := job_category_name (1, name_length);
            job_category^.kind := jmc$profile_category;
            job_category^.index := category_index;
            job_category^.changed := FALSE;
            job_category^.definition_id := 'Category_structure';
            job_category^.attributes.kind := jmc$empty;
            job_category^.references := 0;
            job_category^.next_object := NIL;
            job_category_list [category_index] := job_category;
          IFEND;
        IFEND;
      FOREND;

      attribute.kind := jmc$list;
      ALLOCATE attribute.attribute_list: [1 .. list_size] IN jmv$object_heap^;
      IF attribute.attribute_list = NIL THEN
        jmp$internal_error (95);
      IFEND;
      list_index := 0;
      FOR category_index := 1 TO UPPERBOUND (job_category_list) DO
        IF (category_index - 1) IN categories THEN
          list_index := list_index + 1;
          attribute.attribute_list^ [list_index].kind := jmc$object;
          job_category := job_category_list [category_index];
          attribute.attribute_list^ [list_index].object_p := job_category;
          job_category^.references := job_category^.references + 1;
        IFEND;
      FOREND;
    PROCEND build_category_list;
?? TITLE := 'build_category_objects', EJECT ??

{ PURPOSE:
{   builds the category objects for the profile.
{
{ DESIGN:
{   Fetch the job category data.  Or all elements of the category set
{   array together and build a category object for each element in the
{   category set.

    PROCEDURE build_category_objects
      (VAR status: ost$status);

      VAR
        job_category: jmt$profile_object_reference,
        category_data: jmt$job_category_data,
        categories: jmt$job_category_set,
        cik: jmt$job_category_item_kind,
        category_name: ost$name,
        category_index: integer,
        name_length: integer;

      jmp$get_category_data (category_data, jmv$working_storage, status);

      categories := $jmt$job_category_set [];
      FOR cik := LOWERBOUND (category_data.initial_set_values)
            TO UPPERBOUND (category_data.initial_set_values) DO
        categories := categories + category_data.initial_set_values [cik];
      FOREND;

      FOR category_index := 1 TO UPPERBOUND (job_category_list) DO
        IF (category_index - 1) IN categories THEN
          ALLOCATE job_category IN jmv$object_heap^;
          IF job_category = NIL THEN
            jmp$internal_error (96);
          IFEND;
          job_category^.name := category_data.category_names^ [category_index - 1].name;
          job_category^.kind := jmc$profile_category;
          job_category^.index := category_index;
          job_category^.definition_id := category_data.category_names^ [category_index - 1].definition_name;
          job_category^.attributes.kind := jmc$empty;
          job_category^.references := 0;
          job_category^.changed := FALSE;
          job_category^.next_object := NIL;
          job_category_list [category_index] := job_category;
        ELSE
          job_category_list [category_index] := NIL;
        IFEND;
      FOREND;

    PROCEND build_category_objects;

?? TITLE := ' build_controls_object', EJECT ??

{ PURPOSE:
{   Builds a controls object for the running mainframe.
{
{ DESIGN:
{   Set the name, kind, and definition_id of the object.  Store the attributes
{   from the job_scheduler_table into the controls attribute shell.
{
{ NOTES:
{   The following attribute must be converted back to external units:
{     idle_dispatching_queue_time.

    PROCEDURE build_controls_object
      (VAR controls: jmt$profile_object;
       VAR cpu_quantum_time: ost$task_time_slice;
       VAR status: ost$status);

      VAR
        attributes: jmt$object_attribute,
        controls_attributes: ^jmt$object_attribute_list,
        controls_definition: jmt$profile_declaration,
        dispatching_allocation: ^jmt$object_attribute_list,
        dispatching_priority: jmt$dispatching_priority,
        i: integer,
        job_scheduler_table: jmt$job_scheduler_table,
        mainframe_count: integer,
        other_controls: jmt$profile_object_reference,
        priority_control: ^jmt$object_attribute_list;

      jmp$get_scheduler_table (job_scheduler_table, jmv$working_storage, status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;

      controls.kind := jmc$profile_controls;
      controls.name := job_scheduler_table.validation_categories_p^ [1].mainframe_id;
      controls.changed := FALSE;
      controls.next_object := NIL;
      controls.references := 0;
      controls.index := 1;
      controls_definition := jmv$object_definition [controls.kind].declaration;
      cpu_quantum_time := job_scheduler_table.cpu_quantum_time;
      build_shell (controls_definition, 0, attributes);
      controls_attributes := attributes.attribute_list;

      controls_attributes^ [jmc$ct_profile_identification].name^ :=
            job_scheduler_table.profile_identification;

{   Group definition attributes

      controls_attributes^ [jmc$ct_abbreviation].name^ := 'UNKNOWN';
      controls_attributes^ [jmc$ct_cpu_quantum_time].number := job_scheduler_table.cpu_quantum_time;
      controls_attributes^ [jmc$ct_enable_job_leveling].bool := job_scheduler_table.enable_job_leveling;
      controls_attributes^ [jmc$ct_job_leveling_interval].number := job_scheduler_table.job_leveling_interval;
      controls_attributes^ [jmc$ct_service_calc_interval].number :=
            job_scheduler_table.service_calculation_interval;

{   Group control attributes

      controls_attributes^ [jmc$ct_idle_disp_queue_time].number :=
            job_scheduler_table.idle_dispatching_queue_time DIV microseconds_per_second;
      controls_attributes^ [jmc$ct_scheduling_memory_levels].attribute_list^ [1].
            number := job_scheduler_table.scheduling_memory_levels.target;
      controls_attributes^ [jmc$ct_scheduling_memory_levels].attribute_list^ [2].
            number := job_scheduler_table.scheduling_memory_levels.thrashing;
      controls_attributes^ [jmc$ct_cpu_dispatching_interval].number :=
            job_scheduler_table.dispatching_allocation_interval;

      build_shell (controls_definition.declarations^ [jmc$ct_dispatching_allocation]^, 8,
            controls_attributes^ [jmc$ct_dispatching_allocation]);
      FOR dispatching_priority := jmc$priority_p1 TO jmc$priority_p8 DO
        dispatching_allocation := controls_attributes^ [jmc$ct_dispatching_allocation].
              attribute_list^ [dispatching_priority - dispatching_priority_offset].attribute_list;
        dispatching_allocation^ [1].attribute_list^ [1].number :=
              dispatching_priority - dispatching_priority_offset;
        dispatching_allocation^ [1].attribute_list^ [2].kind := jmc$empty;
        dispatching_allocation^ [2].number := job_scheduler_table.
              cpu_dispatching_allocation [dispatching_priority].minimum;
        dispatching_allocation^ [3].number := job_scheduler_table.
              cpu_dispatching_allocation [dispatching_priority].maximum;
        dispatching_allocation^ [4].bool := job_scheduler_table.
              cpu_dispatching_allocation [dispatching_priority].enforce_maximum;
      FOREND;

      build_shell (controls_definition.declarations^ [jmc$ct_dual_state_prio_control]^, 10,
            controls_attributes^ [jmc$ct_dual_state_prio_control]);
      FOR dispatching_priority := jmc$priority_p1 TO jmc$priority_p10 DO
        priority_control := controls_attributes^ [jmc$ct_dual_state_prio_control].
              attribute_list^ [dispatching_priority - dispatching_priority_offset].attribute_list;
        priority_control^ [1].attribute_list^ [1].number :=
              dispatching_priority - dispatching_priority_offset;
        priority_control^ [1].attribute_list^ [2].kind := jmc$empty;
        priority_control^ [2].number := job_scheduler_table.
              dual_state_priority_control [dispatching_priority].priority;
        priority_control^ [3].number := job_scheduler_table.
              dual_state_priority_control [dispatching_priority].subpriority;
      FOREND;

      build_category_list (job_scheduler_table.initiation_required_categories,
            controls_attributes^ [jmc$ct_ini_required_categories]);
      build_category_list (job_scheduler_table.initiation_excluded_categories,
            controls_attributes^ [jmc$ct_ini_excluded_categories]);

{ Priority Group.

      controls_attributes^ [jmc$ct_job_leveling_prio_bias].number :=
            job_scheduler_table.job_leveling_priority_bias;

{ Membership Group.

      build_category_list (job_scheduler_table.validation_categories_p^ [1].
            required, controls_attributes^ [jmc$ct_val_required_categories]);
      build_category_list (job_scheduler_table.validation_categories_p^ [1].
            excluded, controls_attributes^ [jmc$ct_val_excluded_categories]);

      controls.attributes := attributes;
      mainframe_count := UPPERBOUND (job_scheduler_table.validation_categories_p^);
      FOR i := mainframe_count DOWNTO 2 DO
        ALLOCATE other_controls IN jmv$object_heap^;
        IF other_controls = NIL THEN
          jmp$internal_error (97);
        IFEND;
        other_controls^.kind := jmc$profile_controls;
        other_controls^.name := job_scheduler_table.validation_categories_p^ [i].mainframe_id;
        other_controls^.changed := FALSE;
        other_controls^.index := mainframe_count;
        build_shell (controls_definition, 0, attributes);

        controls_attributes := attributes.attribute_list;

        build_category_list (job_scheduler_table.validation_categories_p^ [i].
              required, controls_attributes^ [jmc$ct_val_required_categories]);
        build_category_list (job_scheduler_table.validation_categories_p^ [i].
              excluded, controls_attributes^ [jmc$ct_val_excluded_categories]);

        controls_attributes^ [jmc$ct_abbreviation].name^ := 'UNKNOWN';
        controls_attributes^ [jmc$ct_profile_identification].name^ := 'NO_PROFILE_IDENTIFICATION';

        other_controls^.attributes := attributes;
        other_controls^.next_object := controls.next_object;
        controls.next_object := other_controls;
      FOREND;
      profile.count [jmc$profile_controls] := mainframe_count;
    PROCEND build_controls_object;
?? TITLE := ' build_job_class_object', EJECT ??

{ PURPOSE:
{   Builds an job class object for the specified index.
{
{ DESIGN:
{   Fetch the job class record for the specified index.  Set the name, kind,
{   and definition_id of the object.  Store the attributes from the job
{   class record into the job class attribute shell.
{
{ NOTES:
{   The service classes must be created first since the job classes reference
{   service classes.
{
{   The following attribute must be converted back to external units:
{     initiation_age_interval.

    PROCEDURE build_job_class_object
      (    job_class_index: jmt$job_class;
       VAR job_class: jmt$profile_object;
       VAR status: ost$status);

      VAR
        job_class_table: jmt$job_class_attributes;

      VAR
        job_class_definition: jmt$profile_declaration,
        job_class_attributes: ^jmt$object_attribute_list,
        delivery_priority: ^jmt$object_attribute_list,
        selection_priority: ^jmt$object_attribute_list,
        detached_job_wait_time: ^jmt$object_attribute_list,
        page_aging_interval: ^jmt$object_attribute_list,
        minimum_working_set: ^jmt$object_attribute_list,
        maximum_working_set: ^jmt$object_attribute_list,
        cyclic_aging_interval: ^jmt$object_attribute_list,
        service_class: jmt$profile_object_reference,
        attribute: jmt$object_attribute;

      jmp$get_job_class_record (job_class_index, job_class_table, jmv$working_storage, status);

      job_class.name := job_class_table.name;
      job_class.definition_id := job_class_table.profile_identification;
      job_class.index := job_class_table.index;
      job_class.kind := jmc$profile_job_class;
      job_class.references := 0;
      job_class.changed := FALSE;
      job_class_definition := jmv$object_definition [job_class.kind].declaration;
      build_shell (job_class_definition, 0, attribute);
      job_class_attributes := attribute.attribute_list;

      job_class_attributes^ [jmc$jc_definition_name].name^ := job_class_table.profile_identification;
      job_class_attributes^ [jmc$jc_index].number := job_class_table.index;
      job_class_attributes^ [jmc$jc_profile_index].number := job_class_table.profile_index;

{   Group definition attributes

      job_class_attributes^ [jmc$jc_abbreviation].name^ := job_class_table.abbreviation;

      IF job_class_table.prolog_p <> NIL THEN
        job_class_attributes^ [jmc$jc_prolog].kind := jmc$file;
        ALLOCATE job_class_attributes^ [jmc$jc_prolog].file: [STRLENGTH (job_class_table.prolog_p^)] IN
              jmv$object_heap^;
        IF job_class_attributes^ [jmc$jc_prolog].file = NIL THEN
          jmp$internal_error (99);
        IFEND;
        job_class_attributes^ [jmc$jc_prolog].file^ := job_class_table.prolog_p^;
      IFEND;
      IF job_class_table.epilog_p <> NIL THEN
        job_class_attributes^ [jmc$jc_epilog].kind := jmc$file;
        ALLOCATE job_class_attributes^ [jmc$jc_epilog].file: [STRLENGTH (job_class_table.epilog_p^)] IN
              jmv$object_heap^;
        IF job_class_attributes^ [jmc$jc_epilog].file = NIL THEN
          jmp$internal_error (99);
        IFEND;
        job_class_attributes^ [jmc$jc_epilog].file^ := job_class_table.epilog_p^;
      IFEND;

      job_class_attributes^ [jmc$jc_enable_class_initiation].bool := job_class_table.enable_class_initiation;
      job_class_attributes^ [jmc$jc_immediate_initiation_can].bool :=
            job_class_table.immediate_initiation_candidate;
      service_class := service_class_list [job_class_table.initial_service_class_index];
      job_class_attributes^ [jmc$jc_initial_service_class].object_p := service_class;
      service_class^.references := service_class^.references + 1;
      job_class_attributes^ [jmc$jc_initial_working_set].number := job_class_table.initial_working_set;

{   Group control attributes

      cyclic_aging_interval := job_class_attributes^ [jmc$jc_cyclic_aging_interval].attribute_list;
      cyclic_aging_interval^ [1].number := job_class_table.cyclic_aging_interval.default;
      cyclic_aging_interval^ [2].number := job_class_table.cyclic_aging_interval.minimum;
      cyclic_aging_interval^ [3].number := job_class_table.cyclic_aging_interval.maximum;

      job_class_attributes^ [jmc$jc_defer_on_submit].bool := job_class_table.defer_on_submit;

      job_class_attributes^ [jmc$jc_initiation_level].attribute_list^ [1].number :=
            job_class_table.initiation_level.preferred;

{     job_class_attributes^ [jmc$jc_initiation_level].attribute_list^ [2].
{           number := job_class_table.initiation_level.maximum_increment;

      maximum_working_set := job_class_attributes^ [jmc$jc_maximum_working_set].attribute_list;
      maximum_working_set^ [1].number := job_class_table.maximum_working_set.default;
      maximum_working_set^ [2].number := job_class_table.maximum_working_set.minimum;
      maximum_working_set^ [3].number := job_class_table.maximum_working_set.maximum;

      minimum_working_set := job_class_attributes^ [jmc$jc_minimum_working_set].attribute_list;
      minimum_working_set^ [1].number := job_class_table.minimum_working_set.default;
      minimum_working_set^ [2].number := job_class_table.minimum_working_set.minimum;
      minimum_working_set^ [3].number := job_class_table.minimum_working_set.maximum;

      page_aging_interval := job_class_attributes^ [jmc$jc_page_aging_interval].attribute_list;
      page_aging_interval^ [1].number := job_class_table.page_aging_interval.default;
      page_aging_interval^ [2].number := job_class_table.page_aging_interval.minimum;
      page_aging_interval^ [3].number := job_class_table.page_aging_interval.maximum;

{   Group limit attributes

      job_class_attributes^ [jmc$jc_cpu_time_limit].number := job_class_table.cpu_time_limit;

      detached_job_wait_time := job_class_attributes^ [jmc$jc_detached_job_wait_time].attribute_list;
      detached_job_wait_time^ [1].number := job_class_table.detached_job_wait_time.default;
      detached_job_wait_time^ [2].number := job_class_table.detached_job_wait_time.minimum;
      detached_job_wait_time^ [3].number := job_class_table.detached_job_wait_time.maximum;
      job_class_attributes^ [jmc$jc_magnetic_tape_limit].number := job_class_table.magnetic_tape_limit;
      job_class_attributes^ [jmc$jc_sru_limit].number := job_class_table.sru_limit;

{   Group membership attributes

      job_class_attributes^ [jmc$jc_auto_class_selection].bool := job_class_table.automatic_class_selection;
      build_category_list (job_class_table.required_categories,
            job_class_attributes^ [jmc$jc_required_categories]);
      build_category_list (job_class_table.excluded_categories,
            job_class_attributes^ [jmc$jc_excluded_categories]);

{   Group priority attributes

      job_class_attributes^ [jmc$jc_initiation_age_interval].number :=
            job_class_table.initiation_age_interval DIV microseconds_per_second;

      job_class_attributes^ [jmc$jc_job_leveling_prio_bias].number :=
            job_class_table.job_leveling_priority_bias;

      job_class_attributes^ [jmc$jc_multiple_job_bias].number := job_class_table.multiple_job_bias;

      selection_priority := job_class_attributes^ [jmc$jc_selection_priority].attribute_list;
      selection_priority^ [1].number := job_class_table.selection_priority.initial;
      selection_priority^ [2].number := job_class_table.selection_priority.maximum;
      selection_priority^ [3].number := job_class_table.selection_priority.increment;
      selection_priority^ [4].number := job_class_table.selection_priority.threshold;

      job_class.attributes := attribute;
    PROCEND build_job_class_object;
?? TITLE := ' build_output_class_object', EJECT ??

{ PURPOSE:
{   Builds an output class object for the specified index.
{
{ DESIGN:
{   Fetch the output class record for the specified index.  Set the name, kind,
{   and definition_id of the object.  Store the attributes from the output
{   class record into the output class attribute shell.

    PROCEDURE build_output_class_object
      (VAR output_class: jmt$profile_object;
       VAR output_class_table: jmt$output_class_attributes);

      VAR
        delivery_priority: ^jmt$object_attribute_list,
        output_class_attributes: ^jmt$object_attribute_list,
        attribute: jmt$object_attribute;

      output_class.name := output_class_table.name;
      output_class.kind := jmc$profile_output_class;
      output_class.definition_id := output_class_table.profile_identification;
      output_class.index := output_class_table.index;
      output_class.references := 0;
      output_class.changed := FALSE;
      build_shell (jmv$object_definition [output_class.kind].declaration, 0, attribute);
      output_class_attributes := attribute.attribute_list;

      output_class_attributes^ [jmc$oc_definition_name].name^ := output_class_table.profile_identification;
      output_class_attributes^ [jmc$oc_index].number := output_class_table.index;

{ Definition Group attributes

      output_class_attributes^ [jmc$oc_enable_class_scheduling].bool :=
            output_class_table.enable_class_scheduling;

{ Priority group attributes

      delivery_priority := output_class_attributes^ [jmc$oc_delivery_priority].attribute_list;
      delivery_priority^ [1].number := output_class_table.delivery_priority.initial;
      delivery_priority^ [2].number := output_class_table.delivery_priority.maximum;
      delivery_priority^ [3].number := output_class_table.delivery_priority.increment;
      output_class_attributes^ [jmc$oc_output_age_interval].number := output_class_table.output_age_interval;

      output_class.attributes := attribute;
    PROCEND build_output_class_object;
?? TITLE := ' build_service_class_object', EJECT ??

{ PURPOSE:
{   Builds an service class object for the specified index.
{
{ DESIGN:
{   Fetch the service class record for the specified index.  Set the name, kind,
{   and definition_id of the object.  Store the attributes from the service
{   class record into the service class attribute shell.
{
{ NOTES:
{   The following attributes must be converted back to external units:
{     swap_age_interval,
{     dispatching_control.service_time,
{     dispatching_control.dispatching_priority,
{     long_wait_think_time.

    PROCEDURE build_service_class_object
      (    service_class_index: jmt$service_class_index;
       VAR service_class: jmt$profile_object;
       VAR status: ost$status);

      VAR
        service_class_table: jmt$service_class_attributes,
        service_class_attributes: ^jmt$object_attribute_list,
        scheduling_priority: ^jmt$object_attribute_list,
        service_factors: ^jmt$object_attribute_list,
        dispatching_control: ^jmt$object_attribute_list,
        dispatching_control_count: jmt$dispatching_control_index,
        dispatching_control_index: jmt$dispatching_control_index,
        next_service_class: jmt$profile_object_reference,
        service_class_definition: jmt$profile_declaration,
        attribute: jmt$object_attribute;

      jmp$get_service_class_record (service_class_index, service_class_table, status);

      service_class.name := service_class_table.name;
      service_class.kind := jmc$profile_service_class;
      service_class.index := service_class_table.index;
      service_class.definition_id := service_class_table.profile_identification;
      service_class.changed := FALSE;
      service_class_definition := jmv$object_definition [service_class.kind].declaration;
      build_shell (service_class_definition, 0, attribute);
      service_class_attributes := attribute.attribute_list;

      service_class_attributes^ [jmc$sc_definition_name].name^ := service_class_table.profile_identification;
      service_class_attributes^ [jmc$sc_index].number := service_class_table.index;

{   Group definition attributes

      service_class_attributes^ [jmc$sc_abbreviation].name^ := service_class_table.abbreviation;
      service_class_attributes^ [jmc$sc_enable_class_execution].bool := TRUE;

{   Group control attributes

      service_class_attributes^ [jmc$sc_aio_limit].number := service_class_table.aio_limit;
      service_class_attributes^ [jmc$sc_attempt_preemption].bool := service_class_table.attempt_preemption;
      service_class_attributes^ [jmc$sc_swap_jobs_in_longwait].bool :=
            service_class_table.swap_jobs_in_longwait;
      service_class_attributes^ [jmc$sc_class_resource_threshold].number :=
            service_class_table.class_service_threshold;
      service_class_attributes^ [jmc$sc_guaranteed_service_quan].number :=
            service_class_table.guaranteed_service_quantum;
      service_class_attributes^ [jmc$sc_long_wait_think_time].number :=
            service_class_table.long_wait_think_time DIV microseconds_per_millisecond;
      service_class_attributes^ [jmc$sc_maximum_active_jobs].number :=
            service_class_table.maximum_active_jobs;

      IF service_class_table.next_service_class_index > 0 THEN
        next_service_class := service_class_list [service_class_table.next_service_class_index];
        service_class_attributes^ [jmc$sc_next_service_class].object_p := next_service_class;
        next_service_class^.references := next_service_class^.references + 1;
      ELSE
        service_class_attributes^ [jmc$sc_next_service_class].kind := jmc$unspecified;
      IFEND;

      service_factors := service_class_attributes^ [jmc$sc_service_factors].attribute_list;
      service_factors^ [1].number := service_class_table.service_factors [jmc$sf_cpu];
      service_factors^ [2].number := service_class_table.service_factors [jmc$sf_memory];
      service_factors^ [3].number := service_class_table.service_factors [jmc$sf_residence];
      service_factors^ [4].number := service_class_table.service_factors [jmc$sf_io];

{   Group priority attributes

      dispatching_control_count := jmc$max_dispatching_control;
      WHILE NOT service_class_table.dispatching_control [dispatching_control_count].set_defined DO
        dispatching_control_count := dispatching_control_count - 1;
      WHILEND;

      build_shell (service_class_definition.declarations^ [jmc$sc_dispatching_control]^,
            dispatching_control_count, attribute.attribute_list^ [jmc$sc_dispatching_control]);

      FOR dispatching_control_index := 1 TO dispatching_control_count DO
        dispatching_control := service_class_attributes^ [jmc$sc_dispatching_control].
              attribute_list^ [dispatching_control_index].attribute_list;
        dispatching_control^ [1].number := service_class_table.
              dispatching_control [dispatching_control_index].dispatching_priority -
              dispatching_priority_offset;
        dispatching_control^ [2].number := service_class_table.
              dispatching_control [dispatching_control_index].service_limit DIV milliseconds_per_second;
        dispatching_control^ [3].number := service_class_table.
              dispatching_control [dispatching_control_index].dispatching_timeslice.minor DIV
              cpu_quantum_time;
        dispatching_control^ [4].number := service_class_table.
              dispatching_control [dispatching_control_index].dispatching_timeslice.major DIV
              cpu_quantum_time;
      FOREND;

      scheduling_priority := service_class_attributes^ [jmc$sc_scheduling_priority].attribute_list;
      scheduling_priority^ [1].number := service_class_table.scheduling_priority.minimum;
      scheduling_priority^ [2].number := service_class_table.scheduling_priority.maximum;
      scheduling_priority^ [3].number := service_class_table.scheduling_priority.swap_age_increment;
      scheduling_priority^ [4].number := service_class_table.scheduling_priority.ready_task_increment;

      service_class_attributes^ [jmc$sc_swap_age_interval].number :=
            service_class_table.swap_age_interval DIV microseconds_per_second;

      service_class.attributes := attribute;
    PROCEND build_service_class_object;
?? OLDTITLE, EJECT ??

    VAR
      category_index: integer,
      class_index: integer,
      list_index: integer,
      class_count: ost$non_negative_integers,
      application: jmt$profile_object_reference,
      job_class: jmt$profile_object_reference,
      service_class: jmt$profile_object_reference,
      job_category: jmt$profile_object_reference,
      object_list_head: jmt$profile_object_reference,
      defined_classes: array [1 .. jmc$maximum_job_classes] of jmt$defined_class;

    FOR class_index := 1 TO UPPERBOUND (job_class_list) DO
      job_class_list [class_index] := NIL;
    FOREND;

    FOR class_index := 1 TO UPPERBOUND (service_class_list) DO
      service_class_list [class_index] := NIL;
    FOREND;

    FOR class_index := 1 TO UPPERBOUND (application_list) DO
      application_list [class_index] := NIL;
    FOREND;

    build_category_objects (status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    ALLOCATE object_list_head IN jmv$object_heap^;
    IF object_list_head = NIL THEN
      jmp$internal_error (100);
    IFEND;
    build_controls_object (object_list_head^, cpu_quantum_time, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;
    profile.objects [jmc$profile_controls] := object_list_head;

{ Build service class objects.

    jmp$get_defined_classes (jmc$service_class, defined_classes, class_count, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    object_list_head := NIL;
    FOR list_index := class_count DOWNTO 1 DO
      class_index := defined_classes [list_index].index;
      ALLOCATE service_class IN jmv$object_heap^;
      IF service_class = NIL THEN
        jmp$internal_error (101);
      IFEND;
      service_class_list [class_index] := service_class;
      service_class^.next_object := object_list_head;
      service_class^.references := 0;
      object_list_head := service_class;
    FOREND;

    FOR list_index := class_count DOWNTO 1 DO
      class_index := defined_classes [list_index].index;
      build_service_class_object (class_index, service_class_list [class_index]^, status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;
    FOREND;
    profile.objects [jmc$profile_service_class] := service_class;
    profile.count [jmc$profile_service_class] := class_count;

{ Build job class objects.

    jmp$get_defined_classes (jmc$job_class, defined_classes, class_count, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    object_list_head := NIL;
    FOR list_index := class_count DOWNTO 1 DO
      class_index := defined_classes [list_index].index;
      ALLOCATE job_class IN jmv$object_heap^;
      IF job_class = NIL THEN
        jmp$internal_error (102);
      IFEND;
      job_class_list [class_index] := job_class;
      build_job_class_object (class_index, job_class^, status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;
      job_class^.next_object := object_list_head;
      object_list_head := job_class;
    FOREND;
    profile.objects [jmc$profile_job_class] := job_class;
    profile.count [jmc$profile_job_class] := class_count;

{ Build application objects.

    jmp$get_defined_classes (jmc$application, defined_classes, class_count, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    object_list_head := NIL;
    FOR list_index := class_count DOWNTO 1 DO
      class_index := defined_classes [list_index].index;
      ALLOCATE application IN jmv$object_heap^;
      IF application = NIL THEN
        jmp$internal_error (103);
      IFEND;
      application_list [class_index] := application;
      build_application_object (defined_classes [list_index].name, class_index, application^, status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;
      application^.next_object := object_list_head;
      object_list_head := application;
    FOREND;
    profile.objects [jmc$profile_application] := object_list_head;
    profile.count [jmc$profile_application] := class_count;

{ Link job category objects into list for profile.

    job_category := NIL;
    class_count := 0;
    FOR class_index := UPPERBOUND (job_category_list) DOWNTO 1 DO
      IF job_category_list [class_index] <> NIL THEN
        job_category_list [class_index]^.next_object := job_category;
        job_category := job_category_list [class_index];
        class_count := class_count + 1;
      IFEND;
    FOREND;
    profile.objects [jmc$profile_category] := job_category;
    profile.count [jmc$profile_category] := class_count;

  PROCEND jmp$build_profile_from_system;
?? TITLE := '[XDCL] jmp$build_tables_from_profile', EJECT ??

{ PURPOSE:
{   This interface builds the scheduling tables from the objects in the
{   scheduling profile.
{
{ DESIGN:
{   Build a scheduling table of the appropriate kind for each object in
{   the scheduling profile.  Tables are build only for those objects which
{   are marked as having changed.
{
{ NOTES:
{   To build the full set of tables, all objects must be marked as changed.

  PROCEDURE [XDCL] jmp$build_tables_from_profile
    (    profile: jmt$profile_data;
         compress: boolean;
     VAR job_class_table: ^jmt$job_class_table;
     VAR service_class_table: ^jmt$service_class_table;
     VAR application_table: ^jmt$application_table;
     VAR job_scheduler_table: jmt$job_scheduler_table;
     VAR job_category_table: jmt$job_category_data;
     VAR status: ost$status);

    VAR
      all_job_categories: jmt$job_category_set;

?? NEWTITLE := ' analyse_job_categories', EJECT ??

{ PURPOSE:
{   This routine builds a set containing all the job categories.
{
{ DESIGN:
{   This category set is build by building a set using the index stored in
{   the object itself as the category bit number.  This index is computed
{   when the profile is installed.

    PROCEDURE analyse_job_categories
      (VAR all_job_categories: jmt$job_category_set);

      VAR
        job_category: jmt$profile_object_reference,
        previous_category_name: ost$name;

      all_job_categories := $jmt$job_category_set [];
      previous_category_name := osc$null_name;

      job_category := profile.objects [jmc$profile_category];
      WHILE job_category <> NIL DO
        IF job_category^.name <> previous_category_name THEN
          all_job_categories := all_job_categories + $jmt$job_category_set [job_category^.index - 1];
          previous_category_name := job_category^.name;
        IFEND;
        job_category := job_category^.next_object;
      WHILEND;

    PROCEND analyse_job_categories;
?? TITLE := ' build_application_table', EJECT ??

{ PURPOSE:
{   This routine builds an application table from the application objects
{   attributes.
{
{ DESIGN:
{   The application attributes are first merged with the defaults and
{   all keyword attributes are converted into constants.  From the resulting
{   attributes the application table is build.

    PROCEDURE build_application_table
      (    application: jmt$profile_object;
       VAR application_table: jmt$application_attributes);

      VAR
        application_attributes: ^jmt$object_attribute_list;

      get_object_attributes (application, application_attributes);

{   Group definition attributes

      application_table.defined := TRUE;
      application_table.profile_identification := application.definition_id;
      application_table.name := application.name;

      application_table.enable_application_scheduling := application_attributes^ [
            jmc$ap_enable_application_sched].bool;

      application_table.service_class_index := 0;
      IF application_attributes^ [jmc$ap_service_class].kind = jmc$object THEN
        application_table.service_class_index := application_attributes^ [jmc$ap_service_class].object_p^.
              index;
      IFEND;

{   Group control attributes

      application_table.cyclic_aging_interval := application_attributes^ [jmc$ap_cyclic_aging_interval].
            number;
      application_table.maximum_working_set := application_attributes^ [jmc$ap_maximum_working_set].number;
      application_table.minimum_working_set := application_attributes^ [jmc$ap_minimum_working_set].number;
      application_table.page_aging_interval := application_attributes^ [jmc$ap_page_aging_interval].number;

    PROCEND build_application_table;
?? TITLE := 'build_category_table', EJECT ??

{ PURPOSE:
{   This routine builds a job category table from the job category object
{   list.

    PROCEDURE build_category_table
      (VAR job_category_table: jmt$job_category_data);

?? NEWTITLE := 'build_item_list', EJECT ??

{ PURPOSE:
{   Builds a sequence of category items from job category attributes.
{
{ DESIGN:
{   Each numeric attribute adds two items to the sequence - one for the
{   lower value and the other for the upper value+1.  Each name attribute
{   adds one item to the sequence.

      PROCEDURE build_item_list
        (VAR item_count: integer;
         VAR maximum_index: integer;
         VAR top_item: ^jmt$job_category_item);

        VAR
          i: integer,
          categories: jmt$job_category_set,
          category_attributes: ^jmt$object_attribute_list,
          attribute: jmt$object_attribute,
          item: ^jmt$job_category_item,
          low_value: integer,
          or_item: jmt$job_category_item,
          last_category_name: ost$name,
          job_category: jmt$profile_object_reference,
          definition: jmt$profile_declaration,
          cik: jmt$job_category_item_kind;

        VAR
          index: [STATIC] array [jmc$ca_cpu_time_limit .. jmc$ca_job_qualifier] of integer :=
                [jmc$c_cpu_time_limit, jmc$c_sru_limit, jmc$c_magnetic_tape_limit, jmc$c_maximum_working_set,
                jmc$c_login_account, jmc$c_login_project, jmc$c_login_family, jmc$c_login_user,
                jmc$c_user_job_name, jmc$c_origin_application_name, jmc$c_job_mode, jmc$c_job_priority,
                jmc$c_job_qualifier];

        job_category := profile.objects [jmc$profile_category];
        last_category_name := ' ';
        maximum_index := 0;

        or_item.kind := jmc$ca_or_conditions;
        or_item.categories := $jmt$job_category_set [];
        or_item.members := or_item.categories;

        item_count := 0;
        NEXT top_item IN jmv$working_storage;
        IF top_item = NIL THEN
          jmp$internal_error (70);
        IFEND;
        top_item^ := or_item;
        top_item^.next_item := #REL (top_item, jmv$working_storage^);
        top_item^.skip_item := #REL (top_item, jmv$working_storage^);

      /for_all_objects/
        WHILE job_category <> NIL DO
          IF maximum_index < (job_category^.index - 1) THEN
            maximum_index := job_category^.index - 1;
          IFEND;

          categories := $jmt$job_category_set [job_category^.index - 1];
          top_item^.members := top_item^.members + categories;

          IF job_category^.attributes.kind <> jmc$type THEN
            job_category := job_category^.next_object;
            CYCLE /for_all_objects/; {----->
          IFEND;

          category_attributes := job_category^.attributes.attribute_list;
          get_object_attributes (job_category^, category_attributes);

{ Create or_item for the previous category if necessary.

          IF last_category_name <> job_category^.name THEN
            IF or_item.categories <> or_item.members THEN
              NEXT item IN jmv$working_storage;
              IF item = NIL THEN
                jmp$internal_error (71);
              IFEND;
              item^ := or_item;
              item^.next_item := top_item^.next_item;
              item^.skip_item := #REL (item, jmv$working_storage^);
              top_item^.next_item := #REL (item, jmv$working_storage^);
              item_count := item_count + 1;
            IFEND;
            last_category_name := job_category^.name;
            or_item.categories := categories;
            or_item.members := categories;
          IFEND;
          or_item.members := or_item.members + categories;

        /for_all_names/
          FOR cik := jmc$ca_login_account TO jmc$ca_job_qualifier DO
            attribute := category_attributes^ [index [cik]];
            IF attribute.kind <> jmc$list THEN
              CYCLE /for_all_names/; {----->
            IFEND;

{ Create category item for each name in the list.

            FOR i := 1 TO UPPERBOUND (attribute.attribute_list^) DO
              NEXT item IN jmv$working_storage;
              IF item = NIL THEN
                jmp$internal_error (72);
              IFEND;
              item^.kind := cik;
              item^.categories := categories;
              item^.name := attribute.attribute_list^ [i].object_p^.name;
              item^.next_item := top_item^.next_item;
              item^.skip_item := #REL (item, jmv$working_storage^);
              top_item^.next_item := #REL (item, jmv$working_storage^);
              item_count := item_count + 1;
            FOREND;
          FOREND /for_all_names/;

        /for_all_numbers/
          FOR cik := LOWERVALUE (cik) TO jmc$ca_working_set DO
            attribute := category_attributes^ [index [cik]];
            IF attribute.kind <> jmc$range THEN
              CYCLE /for_all_numbers/; {----->
            IFEND;

            definition := jmv$object_definition [jmc$profile_category].
                  declaration.declarations^ [index [cik]]^.declarations^ [1]^;

{ Create category item for lower value in range.

            NEXT item IN jmv$working_storage;
            IF item = NIL THEN
              jmp$internal_error (73);
            IFEND;
            item^.kind := cik;
            item^.categories := categories;
            CASE attribute.attribute_list^ [1].kind OF
            = jmc$number =
              item^.number := attribute.attribute_list^ [1].number - 1;
            = jmc$unlimited =
              item^.number := definition.maximum + jmc$unlimited_offset - 1;
            = jmc$unspecified =
              item^.number := definition.maximum + jmc$unspecified_offset - 1;
            CASEND;
            item^.next_item := top_item^.next_item;
            item^.skip_item := #REL (item, jmv$working_storage^);
            top_item^.next_item := #REL (item, jmv$working_storage^);
            low_value := item^.number;

{ Create category item for upper value in range.

            NEXT item IN jmv$working_storage;
            IF item = NIL THEN
              jmp$internal_error (74);
            IFEND;
            item^.kind := cik;
            item^.categories := categories;
            CASE attribute.attribute_list^ [2].kind OF
            = jmc$number =
              item^.number := attribute.attribute_list^ [2].number;
            = jmc$unlimited =
              item^.number := definition.maximum + jmc$unlimited_offset;
            = jmc$unspecified =
              item^.number := definition.maximum + jmc$unspecified_offset;
            = jmc$empty =
              item^.number := low_value + 1;
            CASEND;
            item^.next_item := top_item^.next_item;
            item^.skip_item := #REL (item, jmv$working_storage^);
            top_item^.next_item := #REL (item, jmv$working_storage^);
            item_count := item_count + 2;
          FOREND /for_all_numbers/;
          job_category := job_category^.next_object;
        WHILEND /for_all_objects/;

{ Create or_item for the last category if necessary.

        IF or_item.categories <> or_item.members THEN
          NEXT item IN jmv$working_storage;
          IF item = NIL THEN
            jmp$internal_error (75);
          IFEND;
          item^ := or_item;
          item^.next_item := top_item^.next_item;
          item^.skip_item := #REL (item, jmv$working_storage^);
          top_item^.next_item := #REL (item, jmv$working_storage^);
          item_count := item_count + 1;
        IFEND;

      PROCEND build_item_list;
?? TITLE := 'build_name_list', EJECT ??

{ PURPOSE:
{   Build the list of category names and the corresponding definition names.

      PROCEDURE build_name_list
        (    maximum_index: integer;
         VAR name_list: ^jmt$job_category_name_list);

        VAR
          i: integer,
          job_category: jmt$profile_object_reference;

        job_category := profile.objects [jmc$profile_category];

        IF job_category = NIL THEN
          name_list := NIL;
          RETURN; {----->
        IFEND;

        NEXT name_list: [0 .. maximum_index] IN jmv$working_storage;
        IF name_list = NIL THEN
          jmp$internal_error (76);
        IFEND;
        FOR i := 0 TO maximum_index DO
          name_list^ [i].name := osc$null_name;
          name_list^ [i].definition_name := osc$null_name;
        FOREND;

        WHILE job_category <> NIL DO
          i := job_category^.index - 1;
          name_list^ [i].name := job_category^.name;
          name_list^ [i].definition_name := job_category^.definition_id;
          job_category := job_category^.next_object;
        WHILEND;
      PROCEND build_name_list;

?? TITLE := 'compress_item_list', EJECT ??

{ PURPOSE:
{   Compress the category items and propogate the sets for numeric items.
{
{ DESIGN:
{   This routine removes the duplicate category items combining the set
{   values of the duplicate items into one set.  It also propagates category
{   bits for numeric items.  A category bit will be propagated until it
{   is encountered again in the list at which point it the category bit is
{   removed from the set.

      PROCEDURE compress_item_list
        (VAR total_items: integer;
         VAR top_item: ^jmt$job_category_item);

        VAR
          item: ^jmt$job_category_item,
          item_categories: jmt$job_category_set,
          last_good_item: ^jmt$job_category_item,
          next_item_categories: jmt$job_category_set;

        IF #PTR (top_item^.next_item, jmv$working_storage^) = top_item THEN
          RETURN; {----->
        IFEND;

        last_good_item := top_item;
        item := #PTR (last_good_item^.next_item, jmv$working_storage^);
        WHILE (item <> top_item) AND (item^.kind <= jmc$ca_working_set) DO
          item_categories := next_item_categories;
          IF item^.kind <> last_good_item^.kind THEN
            item_categories := $jmt$job_category_set [];
            last_good_item := item;
          ELSE
            IF item^.number = last_good_item^.number THEN
              total_items := total_items - 1;
              last_good_item^.next_item := item^.next_item;
            ELSE
              last_good_item := item;
            IFEND;
          IFEND;
          next_item_categories := item_categories + item^.categories - (item_categories * item^.categories);
          item^.categories := item_categories;
          item := #PTR (last_good_item^.next_item, jmv$working_storage^);
        WHILEND;

        WHILE (item <> top_item) AND (item^.kind < jmc$ca_or_conditions) DO
          IF (item^.kind = last_good_item^.kind) AND (item^.name = last_good_item^.name) THEN
            total_items := total_items - 1;
            last_good_item^.next_item := item^.next_item;
            last_good_item^.categories := last_good_item^.categories + item^.categories;
          ELSE
            last_good_item := item;
          IFEND;
          item := #PTR (last_good_item^.next_item, jmv$working_storage^);
        WHILEND;
      PROCEND compress_item_list;
?? TITLE := 'copy_item_list', EJECT ??

{ PURPOSE:
{   Builds the final category list from the compressed data.
{
{ DESIGN:
{   This routine copies the data from the compressed list into the final
{   list and links the entries together.

      PROCEDURE copy_item_list
        (    total_items: integer;
             top_item: ^jmt$job_category_item;
         VAR category_data: jmt$job_category_data);

        VAR
          cik: jmt$job_category_item_kind,
          skip_item: ^jmt$job_category_item,
          last_item: ^jmt$job_category_item,
          this_item: ^jmt$job_category_item,
          old_item: ^jmt$job_category_item,
          item_number: integer,
          item_list: ^array [1 .. * ] of jmt$job_category_item;

        FOR cik := LOWERVALUE (cik) TO UPPERVALUE (cik) DO
          category_data.initial_set_values [cik] := top_item^.members;
        FOREND;

        old_item := #PTR (top_item^.next_item, jmv$working_storage^);
        IF old_item = top_item THEN
          job_category_table.item_list := NIL;
          RETURN; {----->
        IFEND;

        NEXT item_list: [1 .. total_items] IN jmv$working_storage;
        IF item_list = NIL THEN
          jmp$internal_error (77);
        IFEND;
        job_category_table.item_list := #SEQ (item_list^);
        RESET job_category_table.item_list;

        item_number := 0;
        WHILE old_item <> top_item DO
          item_number := item_number + 1;
          category_data.initial_set_values [old_item^.kind] :=
                category_data.initial_set_values [old_item^.kind] - old_item^.categories;
          item_list^ [item_number] := old_item^;
          old_item := #PTR (old_item^.next_item, jmv$working_storage^);
        WHILEND;

{ Relink the list in reverse order setting skip_item appropriately.

        skip_item := ^item_list^ [1];
        last_item := skip_item;
        FOR item_number := total_items DOWNTO 1 DO
          this_item := ^item_list^ [item_number];
          IF last_item^.kind <> this_item^.kind THEN
            skip_item := last_item;
          IFEND;
          this_item^.next_item := #REL (last_item, category_data.item_list^);
          this_item^.skip_item := #REL (skip_item, category_data.item_list^);
          last_item := this_item;
        FOREND;

      PROCEND copy_item_list;
?? TITLE := ' sort_item_list', EJECT ??

{ PURPOSE:
{   Put the category records in the sequence into order by kind and value.
{
{ DESIGN:
{   Sort using a merge sort with linked lists.

      PROCEDURE sort_item_list
        (VAR top: ^jmt$job_category_item);

        VAR
          i: integer,
          sublist_size: integer,
          nil_pointer: ^jmt$job_category_item,
          is_less: boolean,
          old_list: ^jmt$job_category_item,
          sublist_1: ^jmt$job_category_item,
          sublist_2: ^jmt$job_category_item,
          newlist_tail: ^jmt$job_category_item,
          sublist_tail: ^jmt$job_category_item;

        old_list := #PTR (top^.next_item, jmv$working_storage^);
        IF old_list = top THEN
          RETURN; {----->
        IFEND;

        nil_pointer := top;

        sublist_size := 1;
        WHILE TRUE DO
          old_list := #PTR (top^.next_item, jmv$working_storage^);
          newlist_tail := top;

          WHILE old_list <> nil_pointer DO

            sublist_1 := old_list;

          /peel1/
            FOR i := 1 TO sublist_size DO
              IF old_list = nil_pointer THEN
                IF newlist_tail = top THEN
                  RETURN; {----->
                IFEND;
                EXIT /peel1/; {----->
              IFEND;
              sublist_tail := old_list;
              old_list := #PTR (old_list^.next_item, jmv$working_storage^);
            FOREND /peel1/;
            sublist_tail^.next_item := #REL (nil_pointer, jmv$working_storage^);

            sublist_2 := old_list;

          /peel2/
            FOR i := 1 TO sublist_size DO
              IF old_list = nil_pointer THEN
                EXIT /peel2/; {----->
              IFEND;
              sublist_tail := old_list;
              old_list := #PTR (old_list^.next_item, jmv$working_storage^);
            FOREND /peel2/;
            sublist_tail^.next_item := #REL (nil_pointer, jmv$working_storage^);

          /merge/
            WHILE (sublist_1 <> nil_pointer) AND (sublist_2 <> nil_pointer) DO
              is_less := FALSE;
              IF sublist_1^.kind < sublist_2^.kind THEN
                is_less := TRUE;
              ELSEIF sublist_1^.kind = sublist_2^.kind THEN
                IF sublist_1^.kind <= jmc$ca_mag_tape_limit THEN
                  is_less := sublist_1^.number < sublist_2^.number;
                ELSE
                  is_less := sublist_1^.name < sublist_2^.name;
                IFEND;
              IFEND;
              IF is_less THEN
                newlist_tail^.next_item := #REL (sublist_1, jmv$working_storage^);
                newlist_tail := sublist_1;
                sublist_1 := #PTR (newlist_tail^.next_item, jmv$working_storage^);
              ELSE
                newlist_tail^.next_item := #REL (sublist_2, jmv$working_storage^);
                newlist_tail := sublist_2;
                sublist_2 := #PTR (newlist_tail^.next_item, jmv$working_storage^);
              IFEND;
            WHILEND /merge/;

            newlist_tail^.next_item := #REL (sublist_1, jmv$working_storage^);
            WHILE sublist_1 <> nil_pointer DO
              newlist_tail := sublist_1;
              sublist_1 := #PTR (newlist_tail^.next_item, jmv$working_storage^);
            WHILEND;

            newlist_tail^.next_item := #REL (sublist_2, jmv$working_storage^);
            WHILE sublist_2 <> nil_pointer DO
              newlist_tail := sublist_2;
              sublist_2 := #PTR (newlist_tail^.next_item, jmv$working_storage^);
            WHILEND;

          WHILEND;
          sublist_size := sublist_size * 2;
        WHILEND;
      PROCEND sort_item_list;
?? OLDTITLE, EJECT ??

      VAR
        top_item: ^jmt$job_category_item,
        maximum_index: integer,
        item_count: integer;

      build_item_list (item_count, maximum_index, top_item);
      sort_item_list (top_item);
      compress_item_list (item_count, top_item);
      copy_item_list (item_count, top_item, job_category_table);
      build_name_list (maximum_index, job_category_table.category_names);
    PROCEND build_category_table;
?? TITLE := ' build_job_category_set', EJECT ??

{ PURPOSE:
{   This routine converts a list of category object references to a category
{   set.
{
{ DESIGN:
{   A category set is build by building a set using the index stored in the
{   object itself as the category bit number.  This index is computed
{   when the profile is installed.

    PROCEDURE build_job_category_set
      (VAR attribute: jmt$object_attribute;
       VAR categories: jmt$job_category_set);

      VAR
        job_category: jmt$profile_object_reference,
        category_list: ^jmt$object_attribute_list,
        i: integer;

      categories := $jmt$job_category_set [];

      IF attribute.kind = jmc$all THEN
        categories := all_job_categories;
        RETURN; {----->
      IFEND;

      IF attribute.kind = jmc$list THEN
        category_list := attribute.attribute_list;
        FOR i := 1 TO UPPERBOUND (category_list^) DO
          IF category_list^ [i].kind = jmc$object THEN
            categories := categories + $jmt$job_category_set [category_list^ [i].object_p^.index - 1];
          IFEND;
        FOREND;
        categories := categories * all_job_categories;
      IFEND;

    PROCEND build_job_category_set;
?? TITLE := ' build_job_class_table', EJECT ??

{ PURPOSE:
{   This routine builds a job class table from the job class object's
{   attributes.
{
{ DESIGN:
{   The job class attributes are first merged with the defaults and
{   all keyword attributes are converted into constants.  From the resulting
{   attributes the job class table is build.
{
{ NOTES:
{   Special processing is done for
{     initiation_level .preferred and .maximum;
{     cpu_time_limit and sru_limit;
{     initiation_age_interval.

    PROCEDURE build_job_class_table
      (    job_class: jmt$profile_object;
       VAR job_class_table: jmt$job_class_attributes);

      VAR
        job_class_attributes: ^jmt$object_attribute_list,
        delivery_priority: ^jmt$object_attribute_list,
        selection_priority: ^jmt$object_attribute_list,
        detached_job_wait_time: ^jmt$object_attribute_list,
        page_aging_interval: ^jmt$object_attribute_list,
        minimum_working_set: ^jmt$object_attribute_list,
        maximum_working_set: ^jmt$object_attribute_list,
        cyclic_aging_interval: ^jmt$object_attribute_list,
        sru_limit: integer,
        cpu_time_limit: integer,
        initiation_level: integer;

      get_object_attributes (job_class, job_class_attributes);

{   Group definition attributes

      job_class_table.defined := TRUE;
      job_class_table.index := job_class.index;
      job_class_table.profile_identification := job_class.definition_id;
      job_class_table.name := job_class.name;

      job_class_table.abbreviation := job_class_attributes^ [jmc$jc_abbreviation].name^;

      job_class_table.prolog_p := NIL;
      job_class_table.epilog_p := NIL;
      IF job_class_attributes^ [jmc$jc_prolog].kind = jmc$file THEN
        NEXT job_class_table.prolog_p: [STRLENGTH (job_class_attributes^ [jmc$jc_prolog].file^)] IN
              jmv$working_storage;
        IF job_class_table.prolog_p = NIL THEN
          jmp$internal_error (78);
        IFEND;
        job_class_table.prolog_p^ := job_class_attributes^ [jmc$jc_prolog].file^;
      IFEND;
      IF job_class_attributes^ [jmc$jc_epilog].kind = jmc$file THEN
        NEXT job_class_table.epilog_p: [STRLENGTH (job_class_attributes^ [jmc$jc_epilog].file^)] IN
              jmv$working_storage;
        IF job_class_table.epilog_p = NIL THEN
          jmp$internal_error (79);
        IFEND;
        job_class_table.epilog_p^ := job_class_attributes^ [jmc$jc_epilog].file^;
      IFEND;

      job_class_table.enable_class_initiation := job_class_attributes^ [jmc$jc_enable_class_initiation].bool;
      job_class_table.immediate_initiation_candidate := job_class_attributes^
            [jmc$jc_immediate_initiation_can].bool;
      job_class_table.default_output_class := 'OUTPUT';
      job_class_table.initial_service_class_index := job_class_attributes^ [jmc$jc_initial_service_class].
            object_p^.index;
      job_class_table.initial_working_set := job_class_attributes^ [jmc$jc_initial_working_set].number;

{   Group control attributes

      job_class_table.defer_on_submit := job_class_attributes^ [jmc$jc_defer_on_submit].bool;

      cyclic_aging_interval := job_class_attributes^ [jmc$jc_cyclic_aging_interval].attribute_list;
      job_class_table.cyclic_aging_interval.default := cyclic_aging_interval^ [1].number;
      job_class_table.cyclic_aging_interval.minimum := cyclic_aging_interval^ [2].number;
      job_class_table.cyclic_aging_interval.maximum := cyclic_aging_interval^ [3].number;

      initiation_level := job_class_attributes^ [jmc$jc_initiation_level].attribute_list^ [1].number;
      IF initiation_level > jmc$unlimited_max_init_jobs THEN
        initiation_level := jmc$unlimited_max_init_jobs;
      IFEND;
      job_class_table.initiation_level.preferred := initiation_level;

{     initiation_level := job_class_attributes^ [jmc$jc_initiation_level].
{           attribute_list^ [2].number;
{     IF initiation_level > jmc$unlimited_max_init_jobs THEN
{       initiation_level := jmc$unlimited_max_init_jobs;
{     IFEND;
{     job_class_table.initiation_level.maximum_increment := initiation_level;

      maximum_working_set := job_class_attributes^ [jmc$jc_maximum_working_set].attribute_list;
      job_class_table.maximum_working_set.default := maximum_working_set^ [1].number;
      job_class_table.maximum_working_set.minimum := maximum_working_set^ [2].number;
      job_class_table.maximum_working_set.maximum := maximum_working_set^ [3].number;

      minimum_working_set := job_class_attributes^ [jmc$jc_minimum_working_set].attribute_list;
      job_class_table.minimum_working_set.default := minimum_working_set^ [1].number;
      job_class_table.minimum_working_set.minimum := minimum_working_set^ [2].number;
      job_class_table.minimum_working_set.maximum := minimum_working_set^ [3].number;

      page_aging_interval := job_class_attributes^ [jmc$jc_page_aging_interval].attribute_list;
      job_class_table.page_aging_interval.default := page_aging_interval^ [1].number;
      job_class_table.page_aging_interval.minimum := page_aging_interval^ [2].number;
      job_class_table.page_aging_interval.maximum := page_aging_interval^ [3].number;

{   Group limit attributes

      cpu_time_limit := job_class_attributes^ [jmc$jc_cpu_time_limit].number;
      IF cpu_time_limit = jmc$highest_cpu_time_limit + jmc$unlimited_offset THEN
        cpu_time_limit := jmc$unlimited_cpu_time_limit;
      IFEND;
      job_class_table.cpu_time_limit := cpu_time_limit;

      detached_job_wait_time := job_class_attributes^ [jmc$jc_detached_job_wait_time].attribute_list;
      job_class_table.detached_job_wait_time.default := detached_job_wait_time^ [1].number;
      job_class_table.detached_job_wait_time.minimum := detached_job_wait_time^ [2].number;
      job_class_table.detached_job_wait_time.maximum := detached_job_wait_time^ [3].number;

      job_class_table.magnetic_tape_limit := job_class_attributes^ [jmc$jc_magnetic_tape_limit].number;

      sru_limit := job_class_attributes^ [jmc$jc_sru_limit].number;
      IF sru_limit = jmc$highest_sru_limit + jmc$unlimited_offset THEN
        sru_limit := jmc$unlimited_sru_limit;
      IFEND;
      job_class_table.sru_limit := sru_limit;

{   Group membership attributes

      job_class_table.next_rank_class := 0;
      job_class_table.automatic_class_selection := job_class_attributes^ [jmc$jc_auto_class_selection].bool;
      build_job_category_set (job_class_attributes^ [jmc$jc_required_categories],
            job_class_table.required_categories);
      build_job_category_set (job_class_attributes^ [jmc$jc_excluded_categories],
            job_class_table.excluded_categories);

{   Group priority attributes

      job_class_table.initiation_age_interval := job_class_attributes^ [jmc$jc_initiation_age_interval].
            number * microseconds_per_second;

      job_class_table.job_leveling_priority_bias := job_class_attributes^ [jmc$jc_job_leveling_prio_bias].
            number;

      job_class_table.multiple_job_bias := job_class_attributes^ [jmc$jc_multiple_job_bias].number;

      selection_priority := job_class_attributes^ [jmc$jc_selection_priority].attribute_list;
      job_class_table.selection_priority.initial := selection_priority^ [1].number;
      job_class_table.selection_priority.maximum := selection_priority^ [2].number;
      job_class_table.selection_priority.increment := selection_priority^ [3].number;
      job_class_table.selection_priority.threshold := selection_priority^ [4].number;

    PROCEND build_job_class_table;
?? TITLE := ' build_output_class_table', EJECT ??

{ PURPOSE:
{   This routine builds an output class table from the output class object's
{   attributes.
{
{ DESIGN:
{   The output class attributes are first merged with the defaults and
{   all keyword attributes are converted into constants.  From the resulting
{   attributes the output class table is build.

    PROCEDURE build_output_class_table
      (    output_class: jmt$profile_object;
       VAR output_class_table: jmt$output_class_attributes);

      VAR
        output_class_attributes: ^jmt$object_attribute_list,
        delivery_priority: ^jmt$object_attribute_list;

      get_object_attributes (output_class, output_class_attributes);

{ Definition Group attributes

      output_class_table.defined := TRUE;
      output_class_table.index := output_class.index;
      output_class_table.profile_identification := output_class.definition_id;
      output_class_table.name := output_class.name;
      output_class_table.enable_class_scheduling := output_class_attributes^ [jmc$oc_enable_class_scheduling].
            bool;

{ Priority group attributes

      delivery_priority := output_class_attributes^ [jmc$oc_delivery_priority].attribute_list;
      output_class_table.delivery_priority.initial := delivery_priority^ [1].number;
      output_class_table.delivery_priority.maximum := delivery_priority^ [2].number;
      output_class_table.delivery_priority.increment := delivery_priority^ [3].number;

      output_class_table.output_age_interval := output_class_attributes^ [jmc$oc_output_age_interval].number;

    PROCEND build_output_class_table;
?? TITLE := ' build_scheduler_table', EJECT ??

{ PURPOSE:
{   This routine builds a scheduler table from the controls object's
{   attribute_list.
{
{ DESIGN:
{   The controls attributes are first merged with the defaults and
{   all keyword attributes are converted into constants.  From the resulting
{   attributes the controls table is build.
{
{ NOTES:
{   Special processing is done for
{     idle_dispatching_queue_time.

    PROCEDURE build_scheduler_table
      (VAR job_scheduler_table: jmt$job_scheduler_table;
       VAR status: ost$status);

      VAR
        controls: jmt$profile_object_reference,
        controls_attributes: ^jmt$object_attribute_list,
        dispatching_allocation: ^jmt$object_attribute_list,
        dispatching_priority: jmt$dispatching_priority,
        i: integer,
        idle_dispatching_queue_time: integer,
        lower_priority: jmt$dispatching_priority,
        mainframe_count: integer,
        priority_control: ^jmt$object_attribute_list,
        upper_priority: jmt$dispatching_priority,
        validation_categories: ^jmt$mainframe_categories;

      status.normal := TRUE;

      controls := profile.objects [jmc$profile_controls];
      get_object_attributes (controls^, controls_attributes);

{ Definition Group

      job_scheduler_table.profile_identification := profile.definition_id;
      job_scheduler_table.cpu_quantum_time := controls_attributes^ [jmc$ct_cpu_quantum_time].number;
      job_scheduler_table.enable_job_leveling := controls_attributes^ [jmc$ct_enable_job_leveling].bool;
      job_scheduler_table.job_leveling_interval := controls_attributes^ [jmc$ct_job_leveling_interval].number;
      job_scheduler_table.service_calculation_interval := controls_attributes^ [jmc$ct_service_calc_interval].
            number;

{ Control group

      idle_dispatching_queue_time := controls_attributes^ [jmc$ct_idle_disp_queue_time].number *
            microseconds_per_second;
      IF idle_dispatching_queue_time = (jmc$highest_idle_disp_q_time + jmc$unlimited_offset *
            microseconds_per_second) THEN
        idle_dispatching_queue_time := jmc$unlimited_idle_disp_q_time;
      IFEND;
      job_scheduler_table.idle_dispatching_queue_time := idle_dispatching_queue_time;
      job_scheduler_table.scheduling_memory_levels.target :=
            controls_attributes^ [jmc$ct_scheduling_memory_levels].attribute_list^ [1].number;
      job_scheduler_table.scheduling_memory_levels.thrashing :=
            controls_attributes^ [jmc$ct_scheduling_memory_levels].attribute_list^ [2].number;
      job_scheduler_table.dispatching_allocation_interval :=
            controls_attributes^ [jmc$ct_cpu_dispatching_interval].number;

      FOR i := 1 TO UPPERBOUND (controls_attributes^ [jmc$ct_dispatching_allocation].attribute_list^) DO
        dispatching_allocation := controls_attributes^ [jmc$ct_dispatching_allocation].attribute_list^ [i].
              attribute_list;
        lower_priority := dispatching_allocation^ [1].attribute_list^ [1].number +
              dispatching_priority_offset;
        upper_priority := lower_priority;
        IF dispatching_allocation^ [1].attribute_list^ [2].kind <> jmc$empty THEN
          upper_priority := dispatching_allocation^ [1].attribute_list^ [2].number +
                dispatching_priority_offset;
        IFEND;
        FOR dispatching_priority := lower_priority TO upper_priority DO
          job_scheduler_table.cpu_dispatching_allocation [dispatching_priority].minimum :=
                dispatching_allocation^ [2].number;
          job_scheduler_table.cpu_dispatching_allocation [dispatching_priority].maximum :=
                dispatching_allocation^ [3].number;
          job_scheduler_table.cpu_dispatching_allocation [dispatching_priority].enforce_maximum :=
                dispatching_allocation^ [4].bool;
        FOREND;
      FOREND;

      FOR i := 1 TO UPPERBOUND (controls_attributes^ [jmc$ct_dual_state_prio_control].attribute_list^) DO
        priority_control := controls_attributes^ [jmc$ct_dual_state_prio_control].attribute_list^ [i].
              attribute_list;
        lower_priority := priority_control^ [1].attribute_list^ [1].number + dispatching_priority_offset;
        upper_priority := lower_priority;
        IF priority_control^ [1].attribute_list^ [2].kind <> jmc$empty THEN
          upper_priority := priority_control^ [1].attribute_list^ [2].number + dispatching_priority_offset;
        IFEND;
        FOR dispatching_priority := lower_priority TO upper_priority DO
          job_scheduler_table.dual_state_priority_control [dispatching_priority].priority :=
                priority_control^ [2].number;
          job_scheduler_table.dual_state_priority_control [dispatching_priority].subpriority :=
                priority_control^ [3].number;
        FOREND;
      FOREND;

      build_job_category_set (controls_attributes^ [jmc$ct_ini_required_categories],
            job_scheduler_table.initiation_required_categories);
      build_job_category_set (controls_attributes^ [jmc$ct_ini_excluded_categories],
            job_scheduler_table.initiation_excluded_categories);

{ Priority group

      job_scheduler_table.job_leveling_priority_bias := controls_attributes^ [jmc$ct_job_leveling_prio_bias].
            number;

{ Membership group

      NEXT validation_categories: [1 .. profile.count [jmc$profile_controls]] IN jmv$working_storage;
      IF validation_categories = NIL THEN
        jmp$internal_error (80);
      IFEND;

      validation_categories^ [1].mainframe_id := controls^.name;
      build_job_category_set (controls_attributes^ [jmc$ct_val_required_categories],
            validation_categories^ [1].required);
      build_job_category_set (controls_attributes^ [jmc$ct_val_excluded_categories],
            validation_categories^ [1].excluded);

      mainframe_count := 1;

      controls := controls^.next_object;
      WHILE controls <> NIL DO
        get_object_attributes (controls^, controls_attributes);
        mainframe_count := mainframe_count + 1;
        validation_categories^ [mainframe_count].mainframe_id := controls^.name;
        build_job_category_set (controls_attributes^ [jmc$ct_val_required_categories],
              validation_categories^ [mainframe_count].required);
        build_job_category_set (controls_attributes^ [jmc$ct_val_excluded_categories],
              validation_categories^ [mainframe_count].excluded);
        controls := controls^.next_object;
      WHILEND;

      NEXT job_scheduler_table.validation_categories_p: [1 .. mainframe_count] IN jmv$working_storage;
      IF job_scheduler_table.validation_categories_p = NIL THEN
        jmp$internal_error (80);
      IFEND;

      FOR i := 1 TO mainframe_count DO
        pmp$convert_mainframe_to_binary (validation_categories^ [i].mainframe_id,
              validation_categories^ [i].binary_mainframe_id, status);
        IF NOT status.normal THEN
          RETURN; {----->
        IFEND;
        job_scheduler_table.validation_categories_p^ [i] := validation_categories^ [i];
      FOREND;

    PROCEND build_scheduler_table;
?? TITLE := ' build_service_class_table', EJECT ??

{ PURPOSE:
{   This routine builds a service class table from the service class object's
{   attributes.
{
{ DESIGN:
{   The service class attributes are first merged with the defaults and
{   all keyword attributes are converted into constants.  From the resulting
{   attributes the service class table is build.
{
{ NOTES:
{   Special processing is done for
{     long_wait_think_time,
{     maximum_active_jobs;
{     dispatching_control .service_limit and .dispatching_priority;
{     swap_age_interval.

    PROCEDURE build_service_class_table
      (    service_class: jmt$profile_object;
       VAR service_class_table: jmt$service_class_attributes);

      VAR
        i: integer,
        service_limit: integer,
        maximum_active_jobs: integer,
        service_class_attributes: ^jmt$object_attribute_list,
        dispatching_control_list: ^jmt$object_attribute_list,
        dispatching_control: ^jmt$object_attribute_list,
        scheduling_priority: ^jmt$object_attribute_list,
        service_factors: ^jmt$object_attribute_list;

      get_object_attributes (service_class, service_class_attributes);

{   Group definition attributes

      service_class_table.defined := TRUE;
      service_class_table.index := service_class.index;
      service_class_table.profile_identification := service_class.definition_id;
      service_class_table.name := service_class.name;

      service_class_table.abbreviation := service_class_attributes^ [jmc$sc_abbreviation].name^;

{   Group control attributes

      service_class_table.aio_limit := service_class_attributes^ [jmc$sc_aio_limit].number;
      service_class_table.attempt_preemption := service_class_attributes^ [jmc$sc_attempt_preemption].bool;
      service_class_table.swap_jobs_in_longwait := service_class_attributes^ [
            jmc$sc_swap_jobs_in_longwait].bool;
      service_class_table.class_service_threshold := service_class_attributes^ [
            jmc$sc_class_resource_threshold].number;
      service_class_table.guaranteed_service_quantum := service_class_attributes^ [
            jmc$sc_guaranteed_service_quan].number;
      service_class_table.long_wait_think_time := service_class_attributes^ [jmc$sc_long_wait_think_time].
            number * microseconds_per_millisecond;

      IF service_class_attributes^ [jmc$sc_enable_class_execution].bool THEN
        maximum_active_jobs := service_class_attributes^ [jmc$sc_maximum_active_jobs].number;
        IF maximum_active_jobs > jmc$unlimited_max_active_jobs THEN
          maximum_active_jobs := jmc$unlimited_max_active_jobs;
        IFEND;
      ELSE
        maximum_active_jobs := 0;
      IFEND;
      service_class_table.maximum_active_jobs := maximum_active_jobs;

      service_class_table.next_service_class_index := 0;
      IF service_class_attributes^ [jmc$sc_next_service_class].kind = jmc$object THEN
        service_class_table.next_service_class_index := service_class_attributes^ [jmc$sc_next_service_class].
              object_p^.index;
      IFEND;

      service_factors := service_class_attributes^ [jmc$sc_service_factors].attribute_list;
      service_class_table.service_factors [jmc$sf_cpu] := service_factors^ [1].number;
      service_class_table.service_factors [jmc$sf_memory] := service_factors^ [2].number;
      service_class_table.service_factors [jmc$sf_residence] := service_factors^ [3].number;
      service_class_table.service_factors [jmc$sf_io] := service_factors^ [4].number;

{   Group priority attributes

      FOR i := 1 TO UPPERBOUND (service_class_table.dispatching_control) DO
        service_class_table.dispatching_control [i].set_defined := FALSE;
      FOREND;

      dispatching_control_list := service_class_attributes^ [jmc$sc_dispatching_control].attribute_list;
      FOR i := 1 TO UPPERBOUND (dispatching_control_list^) DO
        dispatching_control := dispatching_control_list^ [i].attribute_list;
        service_class_table.dispatching_control [i].set_defined := TRUE;
        service_class_table.dispatching_control [i].dispatching_priority :=
              dispatching_control^ [1].number + dispatching_priority_offset;
        service_limit := dispatching_control^ [2].number * milliseconds_per_second;
        IF service_limit = (jmc$highest_service_limit + jmc$unlimited_offset * milliseconds_per_second) THEN
          service_limit := jmc$dc_maximum_service_limit;
        IFEND;
        service_class_table.dispatching_control [i].service_limit := service_limit;
        service_class_table.dispatching_control [i].dispatching_timeslice.minor :=
              job_scheduler_table.cpu_quantum_time * dispatching_control^ [3].number;
        service_class_table.dispatching_control [i].dispatching_timeslice.major :=
              job_scheduler_table.cpu_quantum_time * dispatching_control^ [4].number;
      FOREND;

      scheduling_priority := service_class_attributes^ [jmc$sc_scheduling_priority].attribute_list;
      service_class_table.scheduling_priority.minimum := scheduling_priority^ [1].number;
      service_class_table.scheduling_priority.maximum := scheduling_priority^ [2].number;
      service_class_table.scheduling_priority.swap_age_increment := scheduling_priority^ [3].number;
      service_class_table.scheduling_priority.ready_task_increment := scheduling_priority^ [4].number;

      service_class_table.swap_age_interval := service_class_attributes^ [jmc$sc_swap_age_interval].number *
            microseconds_per_second;

    PROCEND build_service_class_table;
?? TITLE := ' get_object_attributes', EJECT ??

{ PURPOSE:
{   This routine converts the key attributes into the appropriate constants.
{
{ DESIGN:
{   All key attributes are located and converted into appropriate constants
{   for the calling routines convenience.

    PROCEDURE get_object_attributes
      (    object: jmt$profile_object;
       VAR attributes: ^jmt$object_attribute_list);

?? NEWTITLE := 'replace_keywords_with_values', EJECT ??

      PROCEDURE replace_keywords_with_values
        (    definition: jmt$profile_declaration;
         VAR result: jmt$object_attribute);

        TYPE
          attribute_kind_set = set of jmt$object_attribute_kinds;

        VAR
          desired_list_size: integer,
          i: integer,
          keyword_kinds: [STATIC, READ] attribute_kind_set :=
                [jmc$unlimited, jmc$unspecified, jmc$system_default, jmc$none],
          list_kinds: [STATIC, READ] attribute_kind_set := [jmc$list, jmc$range, jmc$editable_list, jmc$type];

        IF result.kind IN list_kinds THEN
          desired_list_size := UPPERBOUND (result.attribute_list^);
          IF result.kind = jmc$type THEN
            FOR i := 1 TO desired_list_size DO
              replace_keywords_with_values (definition.declarations^ [i]^, result.attribute_list^ [i]);
            FOREND;
          ELSE
            FOR i := 1 TO desired_list_size DO
              replace_keywords_with_values (definition.declarations^ [1]^, result.attribute_list^ [i]);
            FOREND;
          IFEND;

        ELSEIF result.kind IN keyword_kinds THEN
          IF (definition.kind = jmc$number) OR (definition.kind = jmc$dispatching_priority) THEN
            CASE result.kind OF
            = jmc$unspecified, jmc$none =
              result.number := definition.maximum + jmc$unspecified_offset;
            = jmc$unlimited =
              result.number := definition.maximum + jmc$unlimited_offset;
            = jmc$system_default =
              result.number := definition.maximum + jmc$system_default_offset;
            CASEND;
            result.kind := definition.kind;

          ELSEIF definition.kind = jmc$name THEN
            NEXT result.name IN jmv$working_storage;
            IF result.name = NIL THEN
              jmp$internal_error (83);
            IFEND;
            CASE result.kind OF
            = jmc$unspecified =
              result.name^ := 'UNSPECIFIED';
            = jmc$unlimited =
              result.name^ := 'UNLIMITED';
            = jmc$none =
              result.name^ := osc$null_name;
            = jmc$system_default =
              result.name^ := 'SYSTEM_DEFAULT';
            CASEND;
            result.kind := definition.kind;
          IFEND;
        IFEND;
      PROCEND replace_keywords_with_values;
?? OLDTITLE, EJECT ??

      VAR
        attribute: jmt$object_attribute,
        ignored_status: ost$status;

      jmp$get_attributes_for_display (profile, object, attribute, ignored_status);
      replace_keywords_with_values (jmv$object_definition [object.kind].declaration, attribute);
      attributes := attribute.attribute_list;

    PROCEND get_object_attributes;
?? OLDTITLE, EJECT ??

    VAR
      application: jmt$profile_object_reference,
      job_class: jmt$profile_object_reference,
      profile_index: jmt$job_class,
      service_class: jmt$profile_object_reference,
      table_size: integer,
      table_index: integer;

    analyse_job_categories (all_job_categories);

    build_scheduler_table (job_scheduler_table, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

{ Build job class scheduling table.

    table_size := 0;
    job_class := profile.objects [jmc$profile_job_class];
    WHILE job_class <> NIL DO
      IF job_class^.changed OR NOT compress THEN
        table_size := table_size + 1;
      IFEND;
      job_class := job_class^.next_object;
    WHILEND;

    job_class_table := NIL;
    IF table_size > 0 THEN
      NEXT job_class_table: [1 .. table_size] IN jmv$working_storage;
      IF job_class_table = NIL THEN
        jmp$internal_error (84);
      IFEND;
      table_index := 0;
      profile_index := 0;
      job_class := profile.objects [jmc$profile_job_class];
      WHILE job_class <> NIL DO
        profile_index := profile_index + 1;
        IF job_class^.changed OR NOT compress THEN
          table_index := table_index + 1;
          build_job_class_table (job_class^, job_class_table^ [table_index]);
          job_class_table^ [table_index].profile_index := profile_index;
        IFEND;
        job_class := job_class^.next_object;
      WHILEND;
    IFEND;

{ Build service class scheduling table.

    table_size := 0;
    service_class := profile.objects [jmc$profile_service_class];
    WHILE service_class <> NIL DO
      IF service_class^.changed OR NOT compress THEN
        table_size := table_size + 1;
      IFEND;
      service_class := service_class^.next_object;
    WHILEND;

    service_class_table := NIL;
    IF table_size <> 0 THEN
      NEXT service_class_table: [1 .. table_size] IN jmv$working_storage;
      IF service_class_table = NIL THEN
        jmp$internal_error (85);
      IFEND;
      table_index := 0;
      service_class := profile.objects [jmc$profile_service_class];
      WHILE service_class <> NIL DO
        IF service_class^.changed OR NOT compress THEN
          table_index := table_index + 1;
          build_service_class_table (service_class^, service_class_table^ [table_index]);
        IFEND;
        service_class := service_class^.next_object;
      WHILEND;
    IFEND;

{ Build application scheduling table.

    table_size := 0;
    application := profile.objects [jmc$profile_application];
    WHILE application <> NIL DO
      IF application^.changed OR NOT compress THEN
        table_size := table_size + 1;
      IFEND;
      application := application^.next_object;
    WHILEND;

    application_table := NIL;
    IF table_size <> 0 THEN
      NEXT application_table: [1 .. table_size] IN jmv$working_storage;
      IF application_table = NIL THEN
        jmp$internal_error (86);
      IFEND;
      table_index := 0;
      application := profile.objects [jmc$profile_application];
      WHILE application <> NIL DO
        IF application^.changed OR NOT compress THEN
          table_index := table_index + 1;
          build_application_table (application^, application_table^ [table_index]);
        IFEND;
        application := application^.next_object;
      WHILEND;
    IFEND;

{ Build job category table.

    IF NOT compress THEN
      build_category_table (job_category_table);
    IFEND;

  PROCEND jmp$build_tables_from_profile;
MODEND jmm$convert_to_scheduler_types;
