?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Job Management: Job History Interfaces' ??
MODULE jmm$process_job_history;
?? PUSH (LISTEXT := ON) ??
*copyc fsc$max_path_size
*copyc fst$evaluated_file_reference
*copyc fst$file_reference
*copyc fst$path_handle_name
*copyc jmc$maximum_output_count
*copyc jmc$system_family
*copyc jme$job_history_conditions
*copyc jml$user_id
*copyc jmt$beginning_log_position
*copyc jmt$jh_descriptive_data
*copyc jmt$job_history_event
*copyc jmt$job_history_job_name_entry
*copyc jmt$job_history_sorted_order
*copyc jmt$name
*copyc oss$job_paged_literal
*copyc ost$user_identification
*copyc pmt$family_name_list
*copyc sfd$type_declarations
*copyc sft$global_log_statistic_header
?? POP ??
*copyc clp$close_display
*copyc clp$convert_str_to_path_handle
*copyc clp$new_display_line
*copyc clp$open_display
*copyc clp$put_display
*copyc clp$reset_for_next_display_page
*copyc clp$trimmed_string_size
*copyc jmp$get_log_entry
*copyc jmp$ready_log_file
*copyc jmp$system_job
*copyc mmp$create_scratch_segment
*copyc mmp$delete_scratch_segment
*copyc osp$disestablish_cond_handler
*copyc osp$establish_block_exit_hndlr
*copyc osp$set_status_condition
*copyc pmp$format_compact_date
*copyc pmp$format_compact_time
*copyc pmp$get_legible_date_time

  TYPE
    formatted_line_range = 1 .. 3,
    formatted_data = record
      line_count: formatted_line_range,
      lines: array [formatted_line_range] of string (osc$max_string_size),
    recend;

?? NEWTITLE := '[XDCL] jmp$process_job_history', EJECT ??

  PROCEDURE [XDCL] jmp$process_job_history
    (    current_control_user: ost$user_identification;
         current_login_user: ost$user_identification;
         requested_sort_order: jmt$job_history_sorted_order;
         trace_job_children: boolean;
         trace_job_output: boolean;
         trace_all_jobs: boolean;
         trace_all_output: boolean;
         display_output_history_command: boolean;
         job_names_requested: ^array [1 .. * ] of ost$name;
         family_names_requested: ^pmt$family_name_list;
         output_files_requested: ^array [1 .. * ] of jmt$name;
         start_log_search: jmt$beginning_log_position;
         output_file: ^fst$file_reference;
         input_file: ^fst$file_reference;
     VAR status: ost$status);


    VAR
      an_event_was_displayed: boolean,
      buffer: sft$statistic_buffer,
      delete_status: ost$status,
      descriptor_p: ^sft$descriptive_data,
      event_count_p: ^ost$non_negative_integers,
      file_id: amt$file_identifier,
      file_position: amt$file_position,
      first_job_event: ^jmt$job_history_event,
      first_job_started: ^jmt$job_history_event,
      header_p: ^sft$global_log_statistic_header,
      job_event_segment_created: boolean,
      job_event_segment_pointer: amt$segment_pointer,
      jobs_to_be_traced: ^jmt$job_history_job_name_entry,
      last_job_event: ^jmt$job_history_event,
      last_job_started: ^jmt$job_history_event,
      last_output_started: ^jmt$job_history_event,
      output_event: boolean,
      queuing_started_entry: boolean,
      scratch_segment_created: boolean,
      scratch_segment_pointer: amt$segment_pointer,
      scratch_sequence_p: ^SEQ ( * ),
      sfn_p: ^jmt$system_supplied_name,
      system_job: boolean,
      valid_entry: boolean;

?? NEWTITLE := 'cleanup_on_block_exit', EJECT ??

{ PURPOSE:
{ The purpose of this request is to cleanup when an unexpected block exit
{ condition occurs.

    PROCEDURE cleanup_on_block_exit
      (    condition: pmt$condition;
           condition_descriptor_p: ^pmt$condition_information;
           stack_frame_save_area_p: ^ost$stack_frame_save_area;
       VAR handler_status: ost$status);

      VAR
        ignore_status: ost$status;

      IF scratch_segment_created THEN
        mmp$delete_scratch_segment (scratch_segment_pointer, ignore_status);
      IFEND;
      IF job_event_segment_created THEN
        mmp$delete_scratch_segment (job_event_segment_pointer, ignore_status);
      IFEND;

    PROCEND cleanup_on_block_exit;
?? OLDTITLE ??
?? EJECT ??
    status.normal := TRUE;

{ Since this code executes in the user ring, the condition handler must be established
{ before any request that could require cleanup is done.

    scratch_segment_created := FALSE;
    job_event_segment_created := FALSE;
    #SPOIL (scratch_segment_created, job_event_segment_created);
    osp$establish_block_exit_hndlr (^cleanup_on_block_exit);

{ Scratch_sequence_p is used when there are output files specified.  The sequence
{ is used to store the unique system supplied name of any output files found.

    #SPOIL (scratch_segment_created);
    scratch_segment_created := TRUE;
    #SPOIL (scratch_segment_created);
    mmp$create_scratch_segment (amc$sequence_pointer, mmc$as_sequential, scratch_segment_pointer, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    scratch_sequence_p := scratch_segment_pointer.sequence_pointer;
    RESET scratch_sequence_p;
    NEXT event_count_p IN scratch_sequence_p;
    event_count_p^ := 0;

    #SPOIL (job_event_segment_created);
    job_event_segment_created := TRUE;
    #SPOIL (job_event_segment_created);
    mmp$create_scratch_segment (amc$sequence_pointer, mmc$as_sequential, job_event_segment_pointer, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    RESET job_event_segment_pointer.sequence_pointer;


    jmp$ready_log_file (start_log_search, current_login_user, input_file, buffer, file_position, header_p,
          descriptor_p, file_id, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    jobs_to_be_traced := NIL;

    NEXT last_job_event IN job_event_segment_pointer.sequence_pointer;
    IF last_job_event = NIL THEN
      osp$set_status_condition (jme$no_space_for_allocate, status);
      RETURN;
    IFEND;

    last_job_event^.next_job := NIL;
    last_job_event^.next_event := NIL;
    first_job_event := last_job_event;

    NEXT last_job_started IN job_event_segment_pointer.sequence_pointer;
    IF last_job_started = NIL THEN
      osp$set_status_condition (jme$no_space_for_allocate, status);
      RETURN;
    IFEND;

    last_job_started^.next_job := NIL;
    last_job_started^.next_event := NIL;

    first_job_started := last_job_started;
    last_output_started := last_job_started;

    system_job := jmp$system_job ();

    WHILE file_position <> amc$eoi DO

      crack_log_entry (current_control_user, current_login_user, header_p, descriptor_p, trace_job_children,
            family_names_requested, job_names_requested, trace_all_jobs, trace_all_output,
            job_event_segment_pointer.sequence_pointer, jobs_to_be_traced, valid_entry, queuing_started_entry,
            scratch_sequence_p, status);

      IF system_job THEN

{ If this is the system job, save all job events; the system job should be able to look
{ at all job histories

        save_log_entry (header_p, descriptor_p, queuing_started_entry, trace_job_output,
              job_event_segment_pointer.sequence_pointer, last_job_event, last_job_started,
              last_output_started, status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;
      ELSE
        IF valid_entry THEN
          IF display_output_history_command THEN
            IF queuing_started_entry AND (header_p^.statistic_code = jml$job_queuing_started) THEN
              save_log_entry (header_p, descriptor_p, queuing_started_entry, trace_job_output,
                    job_event_segment_pointer.sequence_pointer, last_job_event, last_job_started,
                    last_output_started, status);
            ELSE
              trace_output_events (output_files_requested, header_p, descriptor_p, trace_all_output,
                    output_event, scratch_sequence_p);
              IF output_event THEN
                save_log_entry (header_p, descriptor_p, queuing_started_entry, trace_job_output,
                      job_event_segment_pointer.sequence_pointer, last_job_event, last_job_started,
                      last_output_started, status);
                IF NOT status.normal THEN
                  RETURN;
                IFEND;
              IFEND;
            IFEND;
          ELSE { display_job_history command }
            save_log_entry (header_p, descriptor_p, queuing_started_entry, trace_job_output,
                  job_event_segment_pointer.sequence_pointer, last_job_event, last_job_started,
                  last_output_started, status);
            IF NOT status.normal THEN
              RETURN;
            IFEND;
          IFEND;
        IFEND;
      IFEND;

      jmp$get_log_entry (file_id, buffer, file_position, header_p, descriptor_p, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

    WHILEND;

    IF (first_job_started <> last_job_started) OR (first_job_started <> last_output_started) THEN
      display_job_events (requested_sort_order, first_job_event, first_job_started, family_names_requested,
            display_output_history_command, output_file, an_event_was_displayed, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

{ This code handles the case in DISPLAY_OUTPUT_HISTORY where the requested job name is found, but
{ the requested output file name is not found.  It prevents the problem of no output being returned
{ from the command.  Suggestions for a less awkward implementation are welcome.

      IF NOT an_event_was_displayed THEN
        osp$set_status_condition (jme$jh_no_jobs_to_display, status);
      IFEND;
    ELSE
      osp$set_status_condition (jme$jh_no_jobs_to_display, status);
    IFEND;

    mmp$delete_scratch_segment (job_event_segment_pointer, delete_status);
    #SPOIL (job_event_segment_created);
    job_event_segment_created := FALSE;
    #SPOIL (job_event_segment_created);
    mmp$delete_scratch_segment (scratch_segment_pointer, delete_status);
    #SPOIL (scratch_segment_created);
    scratch_segment_created := FALSE;
    #SPOIL (scratch_segment_created);
    osp$disestablish_cond_handler;

  PROCEND jmp$process_job_history;
?? OLDTITLE ??
?? NEWTITLE := 'crack_log_entry', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to dismantle a statistic and retrieve
{   the information needed to determine that an event is part of a job to
{   be traced. Using this information (ie. job_name) it verifies it's to be
{   traced and if so adds the job_event to the internal list of jobs.
{ NOTE:
{   If the statistic is a job_queuing_started or output_queuing_started
{   statistic, the procedure verifies
{   that it meets the trace criteria, ie. the job name is requested or the job
{   belongs to a requested family. If it is not this statistic, the event is
{   checked against the internal list of jobs that have previously been determined
{   to be traced.

  PROCEDURE crack_log_entry
    (    current_control_user: ost$user_identification;
         current_login_user: ost$user_identification;
         header_p: ^sft$global_log_statistic_header;
         descriptor_p: ^sft$descriptive_data;
         trace_job_children: boolean;
         family_names_requested: ^pmt$family_name_list;
         job_names_requested: ^array [1 .. * ] of ost$name;
         trace_all_jobs: boolean;
         trace_all_output: boolean;
     VAR job_event_seq: ^SEQ ( * );
     VAR jobs_to_be_traced: ^jmt$job_history_job_name_entry;
     VAR valid_entry: boolean;
     VAR queuing_started_entry: boolean;
     VAR scratch_sequence_p: ^SEQ ( * );
     VAR status: ost$status);

    VAR
      add_to_job_list: boolean,
      check_job: ^jmt$job_history_job_name_entry,
      data_array: ^array [1 .. * ] of string (osc$max_name_size),
      event_count_p: ^ost$non_negative_integers,
      i: integer,
      job_array_index: integer,
      job_entry: ^jmt$job_history_job_name_entry,
      sfn_p: ^jmt$system_supplied_name;

?? NEWTITLE := 'verify_job_is_to_be_traced', EJECT ??

{ PURPOSE:
{ The purpose of this routine is to verify that a job event is included in the
{ trace criteria, ie. is a given job name or belongs to a given family.

    PROCEDURE verify_job_is_to_be_traced
      (    job_names_requested: ^array [1 .. * ] of ost$name;
           job_name: string (osc$max_name_size);
           user_job_name: string (osc$max_name_size);
           parent_jobs_name: string (osc$max_name_size);
           jobs_to_be_traced: ^jmt$job_history_job_name_entry;
           trace_job_children: boolean;
           family_names_requested: ^pmt$family_name_list;
           login_family: ost$name;
       VAR add_to_job_list: boolean);

      VAR
        i: integer,
        j: integer,
        job_entry: ^jmt$job_history_job_name_entry;

      add_to_job_list := FALSE;

      FOR i := LOWERBOUND (job_names_requested^) TO UPPERBOUND (job_names_requested^) DO
        IF (job_names_requested^ [i] = job_name (1, jmc$system_supplied_name_size)) OR
              (job_names_requested^ [i] = user_job_name (1, osc$max_name_size)) THEN

          FOR j := LOWERBOUND (family_names_requested^) TO UPPERBOUND (family_names_requested^) DO
            IF (family_names_requested^ [j] = login_family) THEN
              add_to_job_list := TRUE;
              RETURN;
            IFEND;
          FOREND;
        IFEND;
      FOREND;
      IF trace_job_children THEN
        job_entry := jobs_to_be_traced;

        WHILE job_entry <> NIL DO
          IF job_entry^.job_name = parent_jobs_name THEN
            FOR j := LOWERBOUND (family_names_requested^) TO UPPERBOUND (family_names_requested^) DO
              IF family_names_requested^ [j] = login_family THEN
                add_to_job_list := TRUE;
                RETURN;
              IFEND;
            FOREND;
          IFEND;
          job_entry := job_entry^.nnext;
        WHILEND;

      IFEND;
    PROCEND verify_job_is_to_be_traced;
?? OLDTITLE ??
?? NEWTITLE := 'verify_output_is_to_be_traced', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to determine if an output event
{   meets the criteria to be traced:
{
{   1. Does the system file name match one of the requested file names?
{   2. Is the output from one of the requested jobs?
{   3. Is the output from one of the requested families?
{
{ NOTE:
{   Output cannot be traced via the output_queuing_started statistic
{   if the user job name instead of the system job name is given for
{   the job name parameter on the DISOH or DISJH command.  There is
{   no user job name field in the output_queuing_started statistic.

    PROCEDURE verify_output_is_to_be_traced
      (    system_file_name: string (osc$max_name_size);
           system_job_name: string (osc$max_name_size);
           job_names_requested: ^array [1 .. * ] of ost$name;
           family_names_requested: ^pmt$family_name_list;
           login_family: ost$name;
       VAR add_to_job_list: boolean;
       VAR scratch_sequence_p: ^SEQ ( * ));

      VAR
        event_count_p: ^ost$non_negative_integers,
        family_array_index: integer,
        file_array_index: integer,
        job_array_index: integer,
        sfn_p: ^jmt$system_supplied_name;

      add_to_job_list := FALSE;
      RESET scratch_sequence_p;
      NEXT event_count_p IN scratch_sequence_p;

    /check_file_names/
      FOR file_array_index := 1 TO event_count_p^ DO
        NEXT sfn_p IN scratch_sequence_p;
        IF (sfn_p^ = system_file_name (1, jmc$system_supplied_name_size)) THEN

          IF job_names_requested = NIL THEN

{ job_names_requested = NIL implies trace_all_jobs = TRUE.

          /check_family_names_all_jobs/
            FOR family_array_index := LOWERBOUND (family_names_requested^)
                  TO UPPERBOUND (family_names_requested^) DO
              IF (family_names_requested^ [family_array_index] = login_family) THEN
                add_to_job_list := TRUE;
                RETURN;
              IFEND;
            FOREND /check_family_names_all_jobs/;
          ELSE

          /check_job_names/
            FOR job_array_index := LOWERBOUND (job_names_requested^) TO UPPERBOUND (job_names_requested^) DO
              IF (job_names_requested^ [job_array_index] = system_job_name (1, jmc$system_supplied_name_size))
                    THEN

              /check_family_names/
                FOR family_array_index := LOWERBOUND (family_names_requested^)
                      TO UPPERBOUND (family_names_requested^) DO
                  IF (family_names_requested^ [family_array_index] = login_family) THEN
                    add_to_job_list := TRUE;
                    RETURN;
                  IFEND;
                FOREND /check_family_names/;
              IFEND;
            FOREND /check_job_names/;
          IFEND;
        IFEND;
      FOREND /check_file_names/;
    PROCEND verify_output_is_to_be_traced;
?? OLDTITLE ??
?? NEWTITLE := 'add_job_to_trace_list', EJECT ??

    PROCEDURE add_job_to_trace_list
      (    job_name: string (osc$max_name_size);
       VAR job_event_seq: ^SEQ ( * );
       VAR jobs_to_be_traced: ^jmt$job_history_job_name_entry;
       VAR status: ost$status);

      VAR
        job_entry: ^jmt$job_history_job_name_entry;

      status.normal := TRUE;

      NEXT job_entry IN job_event_seq;
      IF job_entry = NIL THEN
        osp$set_status_condition (jme$no_space_for_allocate, status);
        RETURN;
      IFEND;

      job_entry^.job_name (1, jmc$system_supplied_name_size) := job_name (1, jmc$system_supplied_name_size);
      job_entry^.nnext := jobs_to_be_traced;
      jobs_to_be_traced := job_entry;

    PROCEND add_job_to_trace_list;
?? OLDTITLE ??
?? EJECT ??

{ Start of Crack_log_entry.

    status.normal := TRUE;

    queuing_started_entry := FALSE;
    valid_entry := FALSE;
    add_to_job_list := FALSE;

{ check if this is the first time the job has been seen and determine if it's to be traced;
{ tracing of a job is determined by the job_queuing_started statistic. If this statistic
{ is not found, a job cannot be traced. Tracing of output routed from another machine is
{ determined by the output_queuing_started statistic.

    IF (header_p^.statistic_code = jml$job_queuing_started) OR
          (header_p^.statistic_code = jml$output_queuing_started) THEN
      queuing_started_entry := TRUE;
      IF header_p^.statistic_code = jml$job_queuing_started THEN

        PUSH data_array: [1 .. jmc$jqs_max_desc_data_fields];
        crack_descriptive_data (jml$job_queuing_started, descriptor_p, data_array);
        IF data_array^ [jmc$jqs_parent_job_name] = jmc$blank_system_supplied_name THEN
          data_array^ [jmc$jqs_parent_job_name] := header_p^.job_name;
        IFEND;

        IF ((current_login_user.user = data_array^ [jmc$jqs_control_user]) AND
              (current_login_user.family = data_array^ [jmc$jqs_control_family])) OR
              ((current_login_user.user = data_array^ [jmc$jqs_login_user]) AND
              (current_login_user.family = data_array^ [jmc$jqs_login_family])) THEN

          IF trace_all_jobs THEN

          /check_for_login_family/
            FOR i := LOWERBOUND (family_names_requested^) TO UPPERBOUND (family_names_requested^) DO
              IF family_names_requested^ [i] = data_array^ [jmc$jqs_login_family] THEN
                add_to_job_list := TRUE;
                EXIT /check_for_login_family/;
              IFEND;
            FOREND /check_for_login_family/;
          ELSE
            verify_job_is_to_be_traced (job_names_requested, data_array^ [jmc$jqs_system_job_name],
                  data_array^ [jmc$jqs_user_job_name], data_array^ [jmc$jqs_parent_job_name],
                  jobs_to_be_traced, trace_job_children, family_names_requested,
                  data_array^ [jmc$jqs_login_family], add_to_job_list);
          IFEND;
          valid_entry := add_to_job_list;

{ Check if a job_queuing_started statistic has already been found for this job.
{ Don't put the job_entry in the trace list twice.

          IF (jobs_to_be_traced <> NIL) AND add_to_job_list THEN
            check_job := jobs_to_be_traced;

          /check_for_job_in_trace_list/
            REPEAT
              IF check_job^.job_name = data_array^ [jmc$jqs_system_job_name] THEN
                add_to_job_list := FALSE;

{ Clear the queuing_started_entry flag to prevent the statistic from being flagged as the start of a new job
{ when it is saved later.

                queuing_started_entry := FALSE;

                EXIT /check_for_job_in_trace_list/;
              IFEND;
              check_job := check_job^.nnext;
            UNTIL check_job = NIL;
          IFEND;
          IF add_to_job_list THEN
            add_job_to_trace_list (data_array^ [jmc$jqs_system_job_name], job_event_seq, jobs_to_be_traced,
                  status);
            IF NOT status.normal THEN
              RETURN;
            IFEND;
            valid_entry := TRUE;
          IFEND;
        IFEND;
      ELSE { output_queuing_started statistic

        PUSH data_array: [1 .. jmc$oqs_max_desc_data_fields];
        crack_descriptive_data (jml$output_queuing_started, descriptor_p, data_array);

        IF ((current_login_user.user = data_array^ [jmc$oqs_control_user]) AND
              (current_login_user.family = data_array^ [jmc$oqs_control_family])) OR
              ((current_login_user.user = data_array^ [jmc$oqs_login_user]) AND
              (current_login_user.family = data_array^ [jmc$oqs_login_family])) THEN

          IF trace_all_output THEN

          /check_oqs_for_login_family/
            FOR i := LOWERBOUND (family_names_requested^) TO UPPERBOUND (family_names_requested^) DO
              IF family_names_requested^ [i] = data_array^ [jmc$oqs_login_family] THEN
                IF job_names_requested <> NIL THEN
                  FOR job_array_index := LOWERBOUND (job_names_requested^)
                        TO UPPERBOUND (job_names_requested^) DO
                    IF job_names_requested^ [job_array_index] = data_array^ [jmc$oqs_system_job_name] THEN
                      add_to_job_list := TRUE;
                      EXIT /check_oqs_for_login_family/;
                    IFEND;
                  FOREND;
                ELSE

{ job_names_requested = NIL implies trace_all_jobs = TRUE.

                  add_to_job_list := TRUE;
                  EXIT /check_oqs_for_login_family/;
                IFEND;
              IFEND;
            FOREND /check_oqs_for_login_family/;
          ELSE
            RESET scratch_sequence_p;
            NEXT event_count_p IN scratch_sequence_p;
            IF event_count_p^ <> 0 THEN
              verify_output_is_to_be_traced (data_array^ [jmc$oqs_system_file_name],
                    data_array^ [jmc$oqs_system_job_name], job_names_requested, family_names_requested,
                    data_array^ [jmc$oqs_login_family], add_to_job_list, scratch_sequence_p);
            IFEND;
          IFEND;
          valid_entry := add_to_job_list;

{ Check if a queuing started statistic has already been found for this output file.
{ Don't put the job_entry in the trace list twice.

          IF (jobs_to_be_traced <> NIL) AND add_to_job_list THEN
            check_job := jobs_to_be_traced;

          /check_for_oqs_job_in_trace_list/
            REPEAT
              IF check_job^.job_name = data_array^ [jmc$oqs_system_job_name] THEN
                add_to_job_list := FALSE;

{ Clear the queuing_started_entry flag to prevent the statistic from being flagged as the start of a new
{ output file when it is saved later.

                queuing_started_entry := FALSE;

                EXIT /check_for_oqs_job_in_trace_list/;
              IFEND;
              check_job := check_job^.nnext;
            UNTIL check_job = NIL;
          IFEND;
          IF add_to_job_list THEN
            add_job_to_trace_list (data_array^ [jmc$oqs_system_job_name], job_event_seq, jobs_to_be_traced,
                  status);
            IF NOT status.normal THEN
              RETURN;
            IFEND;
          IFEND;
        IFEND;
      IFEND;
    IFEND;

    job_entry := jobs_to_be_traced;

    WHILE (NOT valid_entry) AND (job_entry <> NIL) DO

{ If the SJN of the statistic emitter matches the SJN of a job to be traced, the statistic data should be
{ saved.  Omit matching job_queuing_started stats, since they are for child jobs, and were emitted by
{ jmp$submit_job in the parent.  Omit matching job_file_deleted stats, since they are for child jobs which
{ were never initiated, and must not appear in the history of the job that terminated them.
{ This is in lieu of creating a distinct event thread in the event sequence for each job to be traced.

      IF (job_entry^.job_name = header_p^.job_name) AND (header_p^.statistic_code <>
            jml$job_queuing_started) AND (header_p^.statistic_code <> jml$job_file_deleted) THEN

{ The second test in the above IF statement prevents the job_queuing_started entry
{ for child jobs from being displayed when a DISJH of the parent job is entered.

        valid_entry := TRUE;
      ELSE
        check_system_job_stats (job_entry^.job_name, header_p, descriptor_p, valid_entry);
      IFEND;
      job_entry := job_entry^.nnext;
    WHILEND;

  PROCEND crack_log_entry;
?? OLDTITLE ??
?? NEWTITLE := 'check_system_job_stats', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to check the statistics emitted by the
{   system that need to be traced.
{
{ NOTE:
{   Currently, the print_plot_initiated, print_plot_terminated, non_recovery_of_job,
{   job_forwarding_started, output_forwarding_started and change_output_attributes
{   are such statistics, i.e. the system job name in the statistic header is that of
{   the system job.  The name of the job that caused the statistic to be emitted
{   is part of the statistic's descriptive data.

  PROCEDURE check_system_job_stats
    (    job_name: jmt$system_supplied_name;
         header_p: ^sft$global_log_statistic_header;
         descriptor_p: ^sft$descriptive_data;
     VAR valid_entry: boolean);

    VAR
      data_array: ^array [1 .. * ] of string (osc$max_name_size);

    valid_entry := FALSE;

    CASE header_p^.statistic_code OF
    = jml$job_forwarding_started =
      PUSH data_array: [1 .. jmc$jfs_max_desc_data_fields];
      crack_descriptive_data (jml$job_forwarding_started, descriptor_p, data_array);

      IF job_name = data_array^ [jmc$jfs_system_job_name] THEN
        valid_entry := TRUE;
      IFEND;

    = jml$output_forwarding_started =
      PUSH data_array: [1 .. jmc$ofs_max_desc_data_fields];
      crack_descriptive_data (jml$output_forwarding_started, descriptor_p, data_array);

      IF job_name = data_array^ [jmc$ofs_system_job_name] THEN
        valid_entry := TRUE;
      IFEND;

    = jml$output_queuing_started =
      PUSH data_array: [1 .. jmc$oqs_max_desc_data_fields];
      crack_descriptive_data (jml$output_queuing_started, descriptor_p, data_array);

      IF job_name = data_array^ [jmc$oqs_system_job_name] THEN
        valid_entry := TRUE;
      IFEND;

    = jml$print_plot_initiated =
      PUSH data_array: [1 .. jmc$ppi_max_desc_data_fields];
      crack_descriptive_data (jml$print_plot_initiated, descriptor_p, data_array);

      IF job_name = data_array^ [jmc$ppi_system_job_name] THEN
        valid_entry := TRUE;
      IFEND;

    = jml$print_plot_terminated =
      PUSH data_array: [1 .. jmc$ppt_max_desc_data_fields];
      crack_descriptive_data (jml$print_plot_terminated, descriptor_p, data_array);

      IF job_name = data_array^ [jmc$ppt_system_job_name] THEN
        valid_entry := TRUE;
      IFEND;

    = jml$job_file_deleted =
      PUSH data_array: [1 .. jmc$jfd_max_desc_data_fields];
      crack_descriptive_data (jml$job_file_deleted, descriptor_p, data_array);

      IF job_name = data_array^ [jmc$jfd_system_job_name] THEN
        valid_entry := TRUE;
      IFEND;

    = jml$output_file_deleted =
      PUSH data_array: [1 .. jmc$ofd_max_desc_data_fields];
      crack_descriptive_data (jml$output_file_deleted, descriptor_p, data_array);

      IF job_name = data_array^ [jmc$ofd_system_job_name] THEN
        valid_entry := TRUE;
      IFEND;

    = jml$non_recovery_of_job =
      PUSH data_array: [1 .. jmc$nroj_max_desc_data_fields];
      crack_descriptive_data (jml$non_recovery_of_job, descriptor_p, data_array);

      IF job_name = data_array^ [jmc$nroj_system_job_name] THEN
        valid_entry := TRUE;
      IFEND;

    = jml$change_output_attributes =
      PUSH data_array: [1 .. jmc$coa_max_desc_data_fields];
      crack_descriptive_data (jml$change_output_attributes, descriptor_p, data_array);

      IF job_name = data_array^ [jmc$coa_system_job_name] THEN
        valid_entry := TRUE;
      IFEND;
    ELSE

{ do nothing - ignore any statistic other than valid job history statistics

    CASEND;

  PROCEND check_system_job_stats;
?? OLDTITLE ??
?? NEWTITLE := 'crack_descriptive_data', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to extract information from the descriptive data
{   field of the statistic.
{
{ NOTE:
{   Each job history statistic has differenct information in it's descriptive data field
{   This information is defined in the Local_job_history ERS.  This info is formatted
{   at the time the statistic is emitted.  If this information is changed in the system, this
{   procedure would also need to be changed.

  PROCEDURE crack_descriptive_data
    (    statistic_code: sft$statistic_code;
         descriptor_p: ^sft$descriptive_data;
     VAR data_array: ^array [1 .. * ] of string (osc$max_name_size));

    VAR
      data_size: integer;

    data_size := 1;

    CASE statistic_code OF
    = jml$job_queuing_started =
      data_array^ [jmc$jqs_system_job_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$jqs_user_job_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, osc$max_name_size);
      data_size := data_size + osc$max_name_size;

      data_array^ [jmc$jqs_login_family] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, osc$max_name_size);
      data_size := data_size + osc$max_name_size;

      data_array^ [jmc$jqs_login_user] (1, osc$max_name_size) := descriptor_p^ (data_size, osc$max_name_size);
      data_size := data_size + osc$max_name_size;

      data_array^ [jmc$jqs_control_family] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, osc$max_name_size);
      data_size := data_size + osc$max_name_size;

      data_array^ [jmc$jqs_control_user] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, osc$max_name_size);
      data_size := data_size + osc$max_name_size;

      data_array^ [jmc$jqs_station] (1, osc$max_name_size) := descriptor_p^ (data_size, osc$max_name_size);
      data_size := data_size + osc$max_name_size;

{ Conditionally extract the job queuing reason and parent job name.

      IF (data_size + osc$max_name_size - 1) < STRLENGTH (descriptor_p^) THEN
        data_array^ [jmc$jqs_reason] (1, osc$max_name_size) := descriptor_p^ (data_size, osc$max_name_size);
        data_size := data_size + osc$max_name_size;

        data_array^ [jmc$jqs_parent_job_name] (1, osc$max_name_size) :=
              descriptor_p^ (data_size, jmc$system_supplied_name_size);
      ELSE
        data_array^ [jmc$jqs_reason] (1, osc$max_name_size) := osc$null_name;

        data_array^ [jmc$jqs_parent_job_name] (1, osc$max_name_size) := jmc$blank_system_supplied_name;
      IFEND;

    = jml$job_queuing_aborted =

      data_array^ [jmc$jqa_system_job_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$jqa_reason] (1, osc$max_name_size) := descriptor_p^ (data_size, osc$max_name_size);

      data_array^ [jmc$jqa_reason] (1, osc$max_name_size) := descriptor_p^ (data_size, osc$max_name_size);

    = jml$job_file_deleted =

      data_array^ [jmc$jfd_system_job_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$jfd_reason] (1, osc$max_name_size) := descriptor_p^ (data_size, osc$max_name_size);

    = jml$output_queuing_started =

      data_array^ [jmc$oqs_system_job_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$oqs_login_family] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, osc$max_name_size);
      data_size := data_size + osc$max_name_size;

      data_array^ [jmc$oqs_login_user] (1, osc$max_name_size) := descriptor_p^ (data_size, osc$max_name_size);
      data_size := data_size + osc$max_name_size;

      data_array^ [jmc$oqs_system_file_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$oqs_control_family] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, osc$max_name_size);
      data_size := data_size + osc$max_name_size;

      data_array^ [jmc$oqs_control_user] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, osc$max_name_size);
      data_size := data_size + osc$max_name_size;

      data_array^ [jmc$oqs_station] (1, osc$max_name_size) := descriptor_p^ (data_size, osc$max_name_size);
      data_size := data_size + osc$max_name_size;

{ Conditionally extract the output queuing reason.

      IF (data_size + osc$max_name_size - 1) <= STRLENGTH (descriptor_p^) THEN
        data_array^ [jmc$jqs_reason] (1, osc$max_name_size) := descriptor_p^ (data_size, osc$max_name_size);
        data_size := data_size + osc$max_name_size;
      ELSE
        data_array^ [jmc$jqs_reason] (1, osc$max_name_size) := osc$null_name;
      IFEND;

    = jml$output_queuing_aborted =

      data_array^ [jmc$oqa_system_job_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$oqa_system_file_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$oqa_reason] (1, osc$max_name_size) := descriptor_p^ (data_size, osc$max_name_size);

    = jml$output_file_deleted =

      data_array^ [jmc$ofd_system_job_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$ofd_system_file_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$ofd_reason] (1, osc$max_name_size) := descriptor_p^ (data_size, osc$max_name_size);

    = jml$job_forwarding_started =

      data_array^ [jmc$jfs_system_job_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);

    = jml$output_forwarding_started =

      data_array^ [jmc$ofs_system_job_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$ofs_system_file_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$ofs_application_name] := descriptor_p^ (data_size, osc$max_name_size);

    = jml$job_initiated =

      data_array^ [jmc$ji_system_job_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);

    = jml$job_terminated =

      data_array^ [jmc$jt_system_job_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$jt_system_file_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$jt_output_disposition] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, osc$max_name_size);
      data_size := data_size + osc$max_name_size;

      data_array^ [jmc$jt_reason] (1, osc$max_name_size) := descriptor_p^ (data_size, osc$max_name_size);

    = jml$print_plot_initiated =
      data_array^ [jmc$ppi_system_job_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$ppi_system_file_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$ppi_application_name] := descriptor_p^ (data_size, osc$max_name_size);

    = jml$print_plot_terminated =

      data_array^ [jmc$ppt_system_job_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$ppt_system_file_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);

    = jml$submit_job_executed =

      data_array^ [jmc$sje_system_job_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$sje_job_destination] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, osc$max_name_size);
      data_size := data_size + osc$max_name_size;

      data_array^ [jmc$sje_job_destination_usage] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, osc$max_name_size);
      data_size := data_size + osc$max_name_size;

      data_array^ [jmc$sje_user_job_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, osc$max_name_size);

    = jml$print_plot_file_executed =

      data_array^ [jmc$ppfe_system_job_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$ppfe_system_file_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$ppfe_output_destination] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, osc$max_name_size);
      data_size := data_size + osc$max_name_size;

      data_array^ [jmc$ppfe_output_dest_usage] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, osc$max_name_size);
      data_size := data_size + osc$max_name_size;

      data_array^ [jmc$ppfe_user_file_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, osc$max_name_size);

    = jml$job_history_message =

      data_array^ [jmc$jhm_system_job_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);

{ Do not crack message here.  It could be too big to fit
{ easily into data_array^.

    = jml$non_recovery_of_job =

      data_array^ [jmc$nroj_system_job_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$nroj_reason] (1, osc$max_name_size) := descriptor_p^ (data_size, osc$max_name_size);

    = jml$change_output_attributes =

      data_array^ [jmc$coa_system_job_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$coa_system_file_name] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, jmc$system_supplied_name_size);
      data_size := data_size + jmc$system_supplied_name_size;

      data_array^ [jmc$coa_control_family] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, osc$max_name_size);
      data_size := data_size + osc$max_name_size;

      data_array^ [jmc$coa_control_user] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, osc$max_name_size);
      data_size := data_size + osc$max_name_size;

      data_array^ [jmc$coa_output_destination] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, osc$max_name_size);
      data_size := data_size + osc$max_name_size;

      data_array^ [jmc$coa_output_dest_usage] (1, osc$max_name_size) :=
            descriptor_p^ (data_size, osc$max_name_size);
      data_size := data_size + osc$max_name_size;

      data_array^ [jmc$coa_station] (1, osc$max_name_size) := descriptor_p^ (data_size, osc$max_name_size);

    ELSE

{ do nothing - ignore any statistic other than valid job history statistics

    CASEND

  PROCEND crack_descriptive_data;
?? OLDTITLE ??
?? NEWTITLE := 'trace_output_events', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to determine if an output event is
{   from a user requested output file.
{
{   1. Is it an output event.
{   2. Is all output to be printed.
{   3. If the event is from print_plot_file_executed then if the user supplied
{      file name matches the output file requested then save the system supplied name.
{   4. For all other events check for the system_supplied_name field matching an element
{      in the scratch sequence.

  PROCEDURE trace_output_events
    (    output_files_requested: ^array [1 .. * ] of jmt$name;
         header_p: ^sft$global_log_statistic_header;
         descriptor_p: ^sft$descriptive_data;
         trace_all_output: boolean;
     VAR output_event: boolean;
     VAR scratch_sequence_p: ^SEQ ( * ));

    VAR
      data_array: ^array [1 .. * ] of string (osc$max_name_size),
      event_count_p: ^ost$non_negative_integers,
      i: integer,
      sfns_already_assigned_p: ^array [1 .. * ] of jmt$system_supplied_name,
      sfn_p: ^jmt$system_supplied_name;

    output_event := FALSE;
    IF (header_p^.statistic_code = jml$job_terminated) OR
          (header_p^.statistic_code = jml$output_queuing_started) OR
          (header_p^.statistic_code = jml$output_queuing_aborted) OR
          (header_p^.statistic_code = jml$output_file_deleted) OR
          (header_p^.statistic_code = jml$output_forwarding_started) OR
          (header_p^.statistic_code = jml$print_plot_initiated) OR
          (header_p^.statistic_code = jml$print_plot_terminated) OR
          (header_p^.statistic_code = jml$print_plot_file_executed) OR
          (header_p^.statistic_code = jml$change_output_attributes) THEN
      IF trace_all_output THEN
        output_event := TRUE;
        RETURN;
      ELSE {not trace_all_output}
        RESET scratch_sequence_p;
        NEXT event_count_p IN scratch_sequence_p;
        CASE header_p^.statistic_code OF
        = jml$job_terminated =
          PUSH data_array: [1 .. jmc$jt_max_desc_data_fields];
          crack_descriptive_data (header_p^.statistic_code, descriptor_p, data_array);

          FOR i := 1 TO event_count_p^ DO
            NEXT sfn_p IN scratch_sequence_p;
            IF sfn_p^ = data_array^ [jmc$jt_system_file_name] THEN
              output_event := TRUE;
              RETURN;
            IFEND;
          FOREND;

        = jml$output_queuing_started =
          PUSH data_array: [1 .. jmc$oqs_max_desc_data_fields];
          crack_descriptive_data (header_p^.statistic_code, descriptor_p, data_array);

          FOR i := 1 TO event_count_p^ DO
            NEXT sfn_p IN scratch_sequence_p;
            IF sfn_p^ = data_array^ [jmc$oqs_system_file_name] THEN
              output_event := TRUE;
              RETURN;
            IFEND;
          FOREND;

        = jml$output_queuing_aborted =
          PUSH data_array: [1 .. jmc$oqa_max_desc_data_fields];
          crack_descriptive_data (header_p^.statistic_code, descriptor_p, data_array);

          FOR i := 1 TO event_count_p^ DO
            NEXT sfn_p IN scratch_sequence_p;
            IF sfn_p^ = data_array^ [jmc$oqa_system_file_name] THEN
              output_event := TRUE;
              RETURN;
            IFEND;
          FOREND;

        = jml$output_file_deleted =
          PUSH data_array: [1 .. jmc$ofd_max_desc_data_fields];
          crack_descriptive_data (jml$output_file_deleted, descriptor_p, data_array);

          FOR i := 1 TO event_count_p^ DO
            NEXT sfn_p IN scratch_sequence_p;
            IF sfn_p^ = data_array^ [jmc$ofd_system_file_name] THEN
              output_event := TRUE;
              RETURN;
            IFEND;
          FOREND;

        = jml$output_forwarding_started =
          PUSH data_array: [1 .. jmc$ofs_max_desc_data_fields];
          crack_descriptive_data (header_p^.statistic_code, descriptor_p, data_array);

          FOR i := 1 TO event_count_p^ DO
            NEXT sfn_p IN scratch_sequence_p;
            IF sfn_p^ = data_array^ [jmc$ofs_system_file_name] THEN
              output_event := TRUE;
              RETURN;
            IFEND;
          FOREND;

{ This case will determine if the user_supplied_name field in the event equals one of the output files
{ requested, if so add the system supplied name to the scratch sequence.
{ Note:  The user supplied name exists only on the print_plot_file_executed event.  This means the
{ system_supplied_name must be saved for future evaluation to determine if the event is also for this
{ user_supplied_name output.

        = jml$print_plot_file_executed =
          PUSH data_array: [1 .. jmc$ppfe_max_desc_data_fields];
          crack_descriptive_data (header_p^.statistic_code, descriptor_p, data_array);

          FOR i := LOWERBOUND (output_files_requested^) TO UPPERBOUND (output_files_requested^) DO
            IF output_files_requested^ [i].kind = jmc$user_supplied_name THEN
              IF output_files_requested^ [i].user_supplied_name = data_array^ [jmc$ppfe_user_file_name] THEN
                IF event_count_p^ > 0 THEN
                  NEXT sfns_already_assigned_p: [1 .. event_count_p^] IN scratch_sequence_p;
                IFEND;
                NEXT sfn_p IN scratch_sequence_p;
                sfn_p^ := data_array^ [jmc$ppfe_system_file_name];
                event_count_p^ := event_count_p^ +1;
                output_event := TRUE;
                RETURN;
              IFEND;
            IFEND;

            IF output_files_requested^ [i].kind = jmc$system_supplied_name THEN
              IF output_files_requested^ [i].system_supplied_name =
                    data_array^ [jmc$ppfe_system_file_name] THEN
                IF event_count_p^ > 0 THEN
                  NEXT sfns_already_assigned_p: [1 .. event_count_p^] IN scratch_sequence_p;
                IFEND;
                NEXT sfn_p IN scratch_sequence_p;
                sfn_p^ := data_array^ [jmc$ppfe_system_file_name];
                event_count_p^ := event_count_p^ +1;
                output_event := TRUE;
                RETURN;
              IFEND;
            IFEND;

          FOREND;

        = jml$print_plot_initiated =
          PUSH data_array: [1 .. jmc$ppi_max_desc_data_fields];
          crack_descriptive_data (header_p^.statistic_code, descriptor_p, data_array);

          FOR i := 1 TO event_count_p^ DO
            NEXT sfn_p IN scratch_sequence_p;
            IF sfn_p^ = data_array^ [jmc$ppi_system_file_name] THEN
              output_event := TRUE;
              RETURN;
            IFEND;
          FOREND;

        = jml$print_plot_terminated =
          PUSH data_array: [1 .. jmc$ppt_max_desc_data_fields];
          crack_descriptive_data (header_p^.statistic_code, descriptor_p, data_array);

          FOR i := 1 TO event_count_p^ DO
            NEXT sfn_p IN scratch_sequence_p;
            IF sfn_p^ = data_array^ [jmc$ppt_system_file_name] THEN
              output_event := TRUE;
              RETURN;
            IFEND;
          FOREND;

        = jml$change_output_attributes =
          PUSH data_array: [1 .. jmc$coa_max_desc_data_fields];
          crack_descriptive_data (header_p^.statistic_code, descriptor_p, data_array);

          FOR i := 1 TO event_count_p^ DO
            NEXT sfn_p IN scratch_sequence_p;
            IF sfn_p^ = data_array^ [jmc$coa_system_file_name] THEN
              output_event := TRUE;
              RETURN;
            IFEND;
          FOREND;

        ELSE

{ do nothing - ignore any statistic other than valid job history statistics

        CASEND;
      IFEND;

    ELSE
      output_event := FALSE;
    IFEND;

  PROCEND trace_output_events;
?? OLDTITLE ??
?? NEWTITLE := 'save_log_entry', EJECT ??

  PROCEDURE save_log_entry
    (    header_p: ^sft$global_log_statistic_header;
         descriptor_p: ^sft$descriptive_data;
         queuing_started_entry: boolean;
         trace_job_output: boolean;
     VAR job_event_seq: ^SEQ ( * );
     VAR last_job_event: ^jmt$job_history_event;
     VAR last_job_started: ^jmt$job_history_event;
     VAR last_output_started: ^jmt$job_history_event;
     VAR status: ost$status);

    VAR
      job_event: ^jmt$job_history_event;

    status.normal := TRUE;

    IF trace_job_output THEN
      NEXT job_event IN job_event_seq;
      IF job_event = NIL THEN
        osp$set_status_condition (jme$no_space_for_allocate, status);
        RETURN;
      IFEND;

      NEXT job_event^.header IN job_event_seq;
      IF job_event^.header = NIL THEN
        osp$set_status_condition (jme$no_space_for_allocate, status);
        RETURN;
      IFEND;

      job_event^.header^ := header_p^;

      NEXT job_event^.descriptive_data: [header_p^.descriptive_data_size] IN job_event_seq;
      IF job_event^.descriptive_data = NIL THEN
        osp$set_status_condition (jme$no_space_for_allocate, status);
        RETURN;
      IFEND;

      job_event^.descriptive_data^ := descriptor_p^;

      job_event^.next_job := NIL;
      job_event^.next_event := NIL;
      last_job_event^.next_event := job_event;
      last_job_event := job_event;

      IF queuing_started_entry THEN
        IF header_p^.statistic_code = jml$job_queuing_started THEN
          last_job_started^.next_job := job_event;
          last_job_started := job_event;
        ELSE
          IF (last_output_started^.next_job <> NIL) AND (last_output_started^.next_job^.header^.
                statistic_code = jml$job_queuing_started) THEN

{ There must have been a job_queuing_started statistic found before the current
{ output_queuing_started statistic, so we don't need to mark this one.

            ;
          ELSE
            last_output_started^.next_job := job_event;
            last_output_started := job_event;
          IFEND;
        IFEND;
      IFEND;

    ELSE
      IF (header_p^.statistic_code = jml$output_queuing_started) OR
            (header_p^.statistic_code = jml$output_queuing_aborted) OR
            (header_p^.statistic_code = jml$output_forwarding_started) OR
            (header_p^.statistic_code = jml$output_file_deleted) OR
            (header_p^.statistic_code = jml$print_plot_initiated) OR
            (header_p^.statistic_code = jml$print_plot_terminated) OR
            (header_p^.statistic_code = jml$change_output_attributes) THEN
        RETURN;
      ELSE
        NEXT job_event IN job_event_seq;
        IF job_event = NIL THEN
          osp$set_status_condition (jme$no_space_for_allocate, status);
          RETURN;
        IFEND;

        NEXT job_event^.header IN job_event_seq;
        IF job_event^.header = NIL THEN
          osp$set_status_condition (jme$no_space_for_allocate, status);
          RETURN;
        IFEND;

        job_event^.header^ := header_p^;

        NEXT job_event^.descriptive_data: [header_p^.descriptive_data_size] IN job_event_seq;
        IF job_event^.descriptive_data = NIL THEN
          osp$set_status_condition (jme$no_space_for_allocate, status);
          RETURN;
        IFEND;

        job_event^.descriptive_data^ := descriptor_p^;

        job_event^.next_job := NIL;
        job_event^.next_event := NIL;
        last_job_event^.next_event := job_event;
        last_job_event := job_event;

        IF queuing_started_entry THEN
          last_job_started^.next_job := job_event;
          last_job_started := job_event;
        IFEND;

      IFEND;
    IFEND;

  PROCEND save_log_entry;
?? OLDTITLE ??
?? NEWTITLE := 'display_job_events', EJECT ??

  PROCEDURE display_job_events
    (    requested_sort_order: jmt$job_history_sorted_order;
         first_job_event: ^jmt$job_history_event;
         first_job_started: ^jmt$job_history_event;
         family_names_requested: ^array [1 .. * ] of ost$name;
         display_output_history_command: boolean;
         output_file: ^fst$file_reference;
     VAR an_event_was_displayed: boolean;
     VAR status: ost$status);

    VAR
      current_family_name: ost$name,
      current_job_name: jmt$system_supplied_name,
      data_array: ^array [1 .. * ] of string (osc$max_name_size),
      descriptive_data_string: formatted_data,
      delete_allowed: boolean,
      display_control: clt$display_control,
      evaluated_file_reference: fst$evaluated_file_reference,
      header_string: string (80),
      i: integer,
      include_open_pos_in_handle: boolean,
      job_event: ^jmt$job_history_event,
      line_index: formatted_line_range,
      output: clt$file,
      path_handle_name: fst$path_handle_name,
      resolve_path: boolean,
      system_job_name: jmt$system_supplied_name,
      trace_job: ^jmt$job_history_event;

?? NEWTITLE := 'new_page_proc', EJECT ??

    PROCEDURE new_page_proc
      (VAR display_control: clt$display_control;
           new_page_number: integer;
       VAR status: ost$status);

      VAR
        date: ost$date,
        i: integer,
        page_header: string (80),
        time: ost$time;

      clp$reset_for_next_display_page (display_control, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

      page_header := ' ';

      IF display_output_history_command THEN
        page_header (1, 22) := 'DISPLAY OUTPUT HISTORY';
      ELSE
        page_header (1, 19) := 'DISPLAY JOB HISTORY';
      IFEND;

      STRINGREP (page_header (77, * ), i, display_control.page_number);

      pmp$get_legible_date_time (osc$mdy_date, date, osc$ampm_time, time, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

      page_header (50, 8) := date.mdy;
      page_header (40, 8) := time.hms;

      clp$put_display (display_control, page_header, clc$trim, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

      clp$new_display_line (display_control, 2, status);

    PROCEND new_page_proc;
?? OLDTITLE ??
?? NEWTITLE := 'output_current_job', EJECT ??

    PROCEDURE output_current_job
      (    current_job_name: jmt$system_supplied_name;
           job_event: ^jmt$job_history_event;
           display_output_history_command: boolean;
       VAR an_event_was_displayed: boolean;
       VAR status: ost$status);

      VAR
        line_index: formatted_line_range,
        local_job_event: ^jmt$job_history_event,
        system_job_name: jmt$system_supplied_name,
        valid_entry: boolean;

      status.normal := TRUE;

      local_job_event := job_event;

      WHILE local_job_event <> NIL DO

      /display_block/
        BEGIN
          IF NOT ((display_output_history_command) AND (local_job_event^.header^.statistic_code =
                jml$job_queuing_started)) THEN
            check_system_job_stats (current_job_name, local_job_event^.header,
                  local_job_event^.descriptive_data, valid_entry);

            IF (valid_entry) OR ((local_job_event^.header^.job_name = current_job_name) AND
                  (local_job_event^.header^.statistic_code <> jml$job_file_deleted)) OR
                  (local_job_event^.header^.statistic_code = jml$job_queuing_started) THEN

{ Format descriptive data before formatting header in order to get
{ system_job_name from descriptive data.

              format_descriptive_data (local_job_event^.header, local_job_event^.descriptive_data,
                    descriptive_data_string, system_job_name);
              IF (local_job_event^.header^.statistic_code = jml$job_queuing_started) AND
                    (system_job_name <> current_job_name) THEN

{ This is a job_queuing_started statistic for a job other than the current job, so skip it.

                EXIT /display_block/;
              IFEND;
              format_header (local_job_event^.header, system_job_name, header_string, status);
              IF NOT status.normal THEN
                RETURN;
              IFEND;

              clp$put_display (display_control, header_string, clc$trim, status);
              IF NOT status.normal THEN
                RETURN;
              IFEND;
              clp$new_display_line (display_control, 0, status);
              IF NOT status.normal THEN
                RETURN;
              IFEND;

              an_event_was_displayed := TRUE;

              FOR line_index := 1 TO descriptive_data_string.line_count DO
                clp$put_display (display_control, descriptive_data_string.lines [line_index], clc$trim,
                      status);
                IF NOT status.normal THEN
                  RETURN;
                IFEND;

                clp$new_display_line (display_control, 0, status);
                IF NOT status.normal THEN
                  RETURN;
                IFEND;
              FOREND;

              clp$new_display_line (display_control, 1, status);
              IF NOT status.normal THEN
                RETURN;
              IFEND;

            IFEND;
          IFEND;
        END /display_block/;
        local_job_event := local_job_event^.next_event;
      WHILEND;

    PROCEND output_current_job;
?? OLDTITLE ??
?? EJECT ??
    delete_allowed := FALSE;
    resolve_path := TRUE;
    include_open_pos_in_handle := TRUE;
    clp$convert_str_to_path_handle (output_file^, delete_allowed, resolve_path, include_open_pos_in_handle,
          path_handle_name, evaluated_file_reference, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    status.normal := TRUE;
    output.local_file_name := path_handle_name;
    header_string := ' ';
    an_event_was_displayed := FALSE;

    clp$open_display (output, ^new_page_proc, display_control, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    CASE requested_sort_order OF
    = jmc$sort_by_time =

      job_event := first_job_event^.next_event;
      WHILE job_event <> NIL DO

{
{  If the statistic is JOB_QUEUING_STARTED and we are processing the display_output_history
{  command the statistic will not be put into the display.
{

        IF ((NOT display_output_history_command) OR (job_event^.header^.statistic_code <>
              jml$job_queuing_started)) THEN

{ Format descriptive data before formatting header in order to get
{ system_job_name from descriptive data.

          format_descriptive_data (job_event^.header, job_event^.descriptive_data, descriptive_data_string,
                system_job_name);

          format_header (job_event^.header, system_job_name, header_string, status);
          IF NOT status.normal THEN
            RETURN;
          IFEND;
          clp$put_display (display_control, header_string, clc$trim, status);
          IF NOT status.normal THEN
            RETURN;
          IFEND;
          clp$new_display_line (display_control, 0, status);
          IF NOT status.normal THEN
            RETURN;
          IFEND;

          an_event_was_displayed := TRUE;

          FOR line_index := 1 TO descriptive_data_string.line_count DO
            clp$put_display (display_control, descriptive_data_string.lines [line_index], clc$trim, status);
            IF NOT status.normal THEN
              RETURN;
            IFEND;

            clp$new_display_line (display_control, 0, status);
            IF NOT status.normal THEN
              RETURN;
            IFEND;
          FOREND;

          clp$new_display_line (display_control, 1, status);
          IF NOT status.normal THEN
            RETURN;
          IFEND;
        IFEND;

        job_event := job_event^.next_event;
      WHILEND;

    = jmc$sort_by_job =

      trace_job := first_job_started^.next_job;
      job_event := first_job_event^.next_event;

      REPEAT
        IF trace_job^.header^.statistic_code = jml$job_queuing_started THEN
          PUSH data_array: [1 .. jmc$jqs_max_desc_data_fields];
          crack_descriptive_data (trace_job^.header^.statistic_code, trace_job^.descriptive_data, data_array);
          current_job_name := data_array^ [jmc$jqs_system_job_name] (1, jmc$system_supplied_name_size);
        ELSEIF trace_job^.header^.statistic_code = jml$output_queuing_started THEN
          PUSH data_array: [1 .. jmc$oqs_max_desc_data_fields];
          crack_descriptive_data (trace_job^.header^.statistic_code, trace_job^.descriptive_data, data_array);
          current_job_name := data_array^ [jmc$oqs_system_job_name] (1, jmc$system_supplied_name_size);
        ELSE
          osp$set_status_condition (jme$jh_internal_error, status);
          RETURN;
        IFEND;

        output_current_job (current_job_name, job_event, display_output_history_command,
              an_event_was_displayed, status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;

        trace_job := trace_job^.next_job;

      UNTIL trace_job = NIL;

    = jmc$sort_by_family =

      trace_job := first_job_started^.next_job;
      job_event := first_job_event^.next_event;

      FOR i := LOWERBOUND (family_names_requested^) TO UPPERBOUND (family_names_requested^) DO
        REPEAT
          IF trace_job^.header^.statistic_code = jml$job_queuing_started THEN
            PUSH data_array: [1 .. jmc$jqs_max_desc_data_fields];
            crack_descriptive_data (trace_job^.header^.statistic_code, trace_job^.descriptive_data,
                  data_array);
            current_family_name := data_array^ [jmc$jqs_login_family] (1, osc$max_name_size);
            current_job_name := data_array^ [jmc$jqs_system_job_name] (1, jmc$system_supplied_name_size);
          ELSEIF trace_job^.header^.statistic_code = jml$output_queuing_started THEN
            PUSH data_array: [1 .. jmc$oqs_max_desc_data_fields];
            crack_descriptive_data (trace_job^.header^.statistic_code, trace_job^.descriptive_data,
                  data_array);
            current_family_name := data_array^ [jmc$oqs_login_family] (1, osc$max_name_size);
            current_job_name := data_array^ [jmc$oqs_system_job_name] (1, jmc$system_supplied_name_size);
          ELSE
            osp$set_status_condition (jme$jh_internal_error, status);
            RETURN;
          IFEND;
          IF family_names_requested^ [i] = current_family_name THEN

            output_current_job (current_job_name, job_event, display_output_history_command,
                  an_event_was_displayed, status);
            IF NOT status.normal THEN
              RETURN;
            IFEND;

          IFEND;

          trace_job := trace_job^.next_job;
          job_event := trace_job;

        UNTIL trace_job = NIL;
        trace_job := first_job_started^.next_job;
        job_event := first_job_event^.next_event;
      FOREND;

    ELSE
      osp$set_status_condition (jme$jh_internal_error, status);
    CASEND;

    clp$close_display (display_control, status);

  PROCEND display_job_events;
?? OLDTITLE ??
?? NEWTITLE := 'format_header', EJECT ??

  PROCEDURE format_header
    (    header_p: ^sft$global_log_statistic_header;
         system_job_name: jmt$system_supplied_name;
     VAR header_string: string (80);
     VAR status: ost$status);

    VAR
      data_size: 1 .. 80,
      date_value: ost$date,
      event_index: integer,
      event_name: [STATIC, READ, oss$job_paged_literal] array
            [jml$first_history_statistic .. jml$last_history_statistic] of ost$name :=
            ['JOB_QUEUING_STARTED            ', 'JOB_QUEUING_ABORTED            ',
            'OUTPUT_QUEUING_STARTED         ', 'OUTPUT_QUEUING_ABORTED         ',
            'JOB_FORWARDING_STARTED         ', 'OUTPUT_FORWARDING_STARTED      ',
            'JOB_INITIATED                  ', 'JOB_TERMINATED                 ',
            'PRINT_PLOT_INITIATED           ', 'PRINT_PLOT_TERMINATED          ',
            'SUBMIT_JOB_EXECUTED            ', 'PRINT_PLOT_FILE_EXECUTED       ',
            'HISTORY_MESSAGE                ', 'NON_RECOVERY_OF_JOB            ',
            'CHANGE_OUTPUT_ATTRIBUTES       ', 'JOB_FILE_DELETED               ',
            'OUTPUT_FILE_DELETED            '],
      time_value: ost$time;

    status.normal := TRUE;
    data_size := 1;

    header_string (data_size, jmc$system_supplied_name_size) :=
          system_job_name (1, jmc$system_supplied_name_size);
    data_size := data_size + jmc$system_supplied_name_size + 2;

    event_index := header_p^.statistic_code;
    IF (event_index < LOWERBOUND (event_name)) OR (event_index > UPPERBOUND (event_name)) THEN
      RETURN;
    IFEND;
    header_string (data_size, osc$max_name_size) := event_name [event_index];
    data_size := data_size + osc$max_name_size + 2;

    pmp$format_compact_date (header_p^.date_time, osc$mdy_date, date_value, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    header_string (data_size, 8) := date_value.mdy (1, 8);
    data_size := data_size + 10;

    pmp$format_compact_time (header_p^.date_time, osc$hms_time, time_value, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    header_string (data_size, 8) := time_value.millisecond (1, 8);

  PROCEND format_header;
?? OLDTITLE ??
?? NEWTITLE := 'format_descriptive_data', EJECT ??

  PROCEDURE format_descriptive_data
    (    header: ^sft$global_log_statistic_header;
         descriptive_data: ^sft$descriptive_data;
     VAR data_string: formatted_data;
     VAR system_job_name: jmt$system_supplied_name);


    VAR
      data_array: ^array [1 .. * ] of string (osc$max_name_size),
      data_size: 1 .. osc$max_string_size,
      line_index: formatted_line_range,
      message_size: 1 .. osc$max_string_size,
      str_size: 0 .. osc$max_name_size;

    data_string.line_count := 1;
    FOR line_index := 1 TO UPPERBOUND (data_string.lines) DO
      data_string.lines [line_index] := '';
    FOREND;
    data_size := 1;
    system_job_name := jmc$blank_system_supplied_name;

{ Don't put the system_job_name into the data_string; it goes into the header_string.
{ The one exception to this is the submit_job_executed statistic, in which the
{ system_job_name in the descriptive data is that of the submitted job so it goes
{ into the data_string.

    CASE header^.statistic_code OF
    = jml$job_queuing_started =

      PUSH data_array: [1 .. jmc$jqs_max_desc_data_fields];
      crack_descriptive_data (header^.statistic_code, descriptive_data, data_array);

      system_job_name := data_array^ [jmc$jqs_system_job_name];
      data_string.line_count := 2;

      data_string.lines [1] (data_size, jmc$system_supplied_name_size) := jmc$blank_system_supplied_name;
      data_size := data_size + jmc$system_supplied_name_size;

      data_string.lines [1] (data_size, 4) := 'UJN=';
      data_size := data_size + 4;

      str_size := clp$trimmed_string_size (data_array^ [jmc$jqs_user_job_name]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$jqs_user_job_name] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 14) := ', LOGIN_USER=:';
      data_size := data_size + 14;

      str_size := clp$trimmed_string_size (data_array^ [jmc$jqs_login_family]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$jqs_login_family] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 1) := '.';
      data_size := data_size + 1;

      str_size := clp$trimmed_string_size (data_array^ [jmc$jqs_login_user]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$jqs_login_user] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 1) := ',';

      data_size := 1;

      data_string.lines [2] (data_size, jmc$system_supplied_name_size) := jmc$blank_system_supplied_name;
      data_size := data_size + jmc$system_supplied_name_size;

      data_string.lines [2] (data_size, 14) := 'CONTROL_USER=:';
      data_size := data_size + 14;

      str_size := clp$trimmed_string_size (data_array^ [jmc$jqs_control_family]);
      data_string.lines [2] (data_size, str_size) := data_array^ [jmc$jqs_control_family] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [2] (data_size, 1) := '.';
      data_size := data_size + 1;

      str_size := clp$trimmed_string_size (data_array^ [jmc$jqs_control_user]);
      data_string.lines [2] (data_size, str_size) := data_array^ [jmc$jqs_control_user] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [2] (data_size, 10) := ', STATION=';
      data_size := data_size + 10;

      str_size := clp$trimmed_string_size (data_array^ [jmc$jqs_station]);
      data_string.lines [2] (data_size, str_size) := data_array^ [jmc$jqs_station] (1, str_size);
      data_size := data_size + str_size;

      IF data_array^ [jmc$jqs_reason] <> osc$null_name THEN
        data_string.line_count := 3;
        data_size := 1;

        data_string.lines [2] (data_size, jmc$system_supplied_name_size) := jmc$blank_system_supplied_name;
        data_size := data_size + jmc$system_supplied_name_size;

        data_string.lines [3] (data_size, 7) := 'REASON=';
        data_size := data_size + 7;

        str_size := clp$trimmed_string_size (data_array^ [jmc$jqs_reason]);
        data_string.lines [3] (data_size, str_size) := data_array^ [jmc$jqs_reason] (1, str_size);
        data_size := data_size + str_size;
      IFEND;

    = jml$job_queuing_aborted =

      PUSH data_array: [1 .. jmc$jqa_max_desc_data_fields];
      crack_descriptive_data (header^.statistic_code, descriptive_data, data_array);

      system_job_name := data_array^ [jmc$jqa_system_job_name];
      data_string.lines [1] (data_size, jmc$system_supplied_name_size) := jmc$blank_system_supplied_name;
      data_size := data_size + jmc$system_supplied_name_size;

      data_string.lines [1] (data_size, 7) := 'REASON=';
      data_size := data_size + 7;

      str_size := clp$trimmed_string_size (data_array^ [jmc$jqa_reason]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$jqa_reason] (1, str_size);
      data_size := data_size + str_size;

    = jml$job_file_deleted =

      PUSH data_array: [1 .. jmc$jfd_max_desc_data_fields];
      crack_descriptive_data (header^.statistic_code, descriptive_data, data_array);

      system_job_name := data_array^ [jmc$jfd_system_job_name];
      data_string.lines [1] (data_size, jmc$system_supplied_name_size) := jmc$blank_system_supplied_name;
      data_size := data_size + jmc$system_supplied_name_size;

      data_string.lines [1] (data_size, 7) := 'REASON=';
      data_size := data_size + 7;

      str_size := clp$trimmed_string_size (data_array^ [jmc$jfd_reason]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$jfd_reason] (1, str_size);
      data_size := data_size + str_size;

    = jml$output_queuing_started =

      PUSH data_array: [1 .. jmc$oqs_max_desc_data_fields];
      crack_descriptive_data (header^.statistic_code, descriptive_data, data_array);

      system_job_name := data_array^ [jmc$oqs_system_job_name];
      data_string.line_count := 2;

      data_string.lines [1] (data_size, jmc$system_supplied_name_size) := jmc$blank_system_supplied_name;
      data_size := data_size + jmc$system_supplied_name_size;

      data_string.lines [1] (data_size, 12) := 'LOGIN_USER=:';
      data_size := data_size + 12;

      str_size := clp$trimmed_string_size (data_array^ [jmc$oqs_login_family]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$oqs_login_family] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 1) := '.';
      data_size := data_size + 1;

      str_size := clp$trimmed_string_size (data_array^ [jmc$oqs_login_user]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$oqs_login_user] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 6) := ', SFN=';
      data_size := data_size + 6;

      str_size := clp$trimmed_string_size (data_array^ [jmc$oqs_system_file_name]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$oqs_system_file_name] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 1) := ',';

      data_size := 1;

      data_string.lines [2] (data_size, jmc$system_supplied_name_size) := jmc$blank_system_supplied_name;
      data_size := data_size + jmc$system_supplied_name_size;

      data_string.lines [2] (data_size, 14) := 'CONTROL_USER=:';
      data_size := data_size + 14;

      str_size := clp$trimmed_string_size (data_array^ [jmc$oqs_control_family]);
      data_string.lines [2] (data_size, str_size) := data_array^ [jmc$oqs_control_family] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [2] (data_size, 1) := '.';
      data_size := data_size + 1;

      str_size := clp$trimmed_string_size (data_array^ [jmc$oqs_control_user]);
      data_string.lines [2] (data_size, str_size) := data_array^ [jmc$oqs_control_user] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [2] (data_size, 10) := ', STATION=';
      data_size := data_size + 10;

      str_size := clp$trimmed_string_size (data_array^ [jmc$oqs_station]);
      data_string.lines [2] (data_size, str_size) := data_array^ [jmc$oqs_station] (1, str_size);
      data_size := data_size + str_size;

      IF data_array^ [jmc$oqs_reason] <> osc$null_name THEN
        data_string.line_count := 3;
        data_size := 1;

        data_string.lines [2] (data_size, jmc$system_supplied_name_size) := jmc$blank_system_supplied_name;
        data_size := data_size + jmc$system_supplied_name_size;

        data_string.lines [3] (data_size, 7) := 'REASON=';
        data_size := data_size + 7;

        str_size := clp$trimmed_string_size (data_array^ [jmc$oqs_reason]);
        data_string.lines [3] (data_size, str_size) := data_array^ [jmc$oqs_reason] (1, str_size);
        data_size := data_size + str_size;
      IFEND;

    = jml$output_queuing_aborted =

      PUSH data_array: [1 .. jmc$oqa_max_desc_data_fields];
      crack_descriptive_data (header^.statistic_code, descriptive_data, data_array);

      system_job_name := data_array^ [jmc$oqa_system_job_name];
      data_string.lines [1] (data_size, jmc$system_supplied_name_size) := jmc$blank_system_supplied_name;
      data_size := data_size + jmc$system_supplied_name_size;

      data_string.lines [1] (data_size, 4) := 'SFN=';
      data_size := data_size + 4;

      str_size := clp$trimmed_string_size (data_array^ [jmc$oqa_system_file_name]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$oqa_system_file_name] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 9) := ', REASON=';
      data_size := data_size + 9;

      str_size := clp$trimmed_string_size (data_array^ [jmc$oqa_reason]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$oqa_reason] (1, str_size);
      data_size := data_size + str_size;

    = jml$output_file_deleted =

      PUSH data_array: [1 .. jmc$ofd_max_desc_data_fields];
      crack_descriptive_data (header^.statistic_code, descriptive_data, data_array);

      system_job_name := data_array^ [jmc$ofd_system_job_name];
      data_string.lines [1] (data_size, jmc$system_supplied_name_size) := jmc$blank_system_supplied_name;
      data_size := data_size + jmc$system_supplied_name_size;

      data_string.lines [1] (data_size, 4) := 'SFN=';
      data_size := data_size + 4;

      str_size := clp$trimmed_string_size (data_array^ [jmc$ofd_system_file_name]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$ofd_system_file_name] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 9) := ', REASON=';
      data_size := data_size + 9;

      str_size := clp$trimmed_string_size (data_array^ [jmc$ofd_reason]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$ofd_reason] (1, str_size);
      data_size := data_size + str_size;

    = jml$job_forwarding_started =

      PUSH data_array: [1 .. jmc$jfs_max_desc_data_fields];
      crack_descriptive_data (header^.statistic_code, descriptive_data, data_array);

      system_job_name := data_array^ [jmc$jfs_system_job_name];

    = jml$output_forwarding_started =

      PUSH data_array: [1 .. jmc$ofs_max_desc_data_fields];
      crack_descriptive_data (header^.statistic_code, descriptive_data, data_array);

      system_job_name := data_array^ [jmc$ofs_system_job_name];
      data_string.lines [1] (data_size, jmc$system_supplied_name_size) := jmc$blank_system_supplied_name;
      data_size := data_size + jmc$system_supplied_name_size;

      data_string.lines [1] (data_size, 4) := 'SFN=';
      data_size := data_size + 4;

      str_size := clp$trimmed_string_size (data_array^ [jmc$ofs_system_file_name]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$ofs_system_file_name] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 11) := ', APP_NAME=';
      data_size := data_size + 11;

      str_size := clp$trimmed_string_size (data_array^ [jmc$ofs_application_name]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$ofs_application_name] (1, str_size);
      data_size := data_size + str_size;

    = jml$job_initiated =

      PUSH data_array: [1 .. jmc$ji_max_desc_data_fields];
      crack_descriptive_data (header^.statistic_code, descriptive_data, data_array);

      system_job_name := data_array^ [jmc$ji_system_job_name];

    = jml$job_terminated =

      PUSH data_array: [1 .. jmc$jt_max_desc_data_fields];
      crack_descriptive_data (header^.statistic_code, descriptive_data, data_array);

      system_job_name := data_array^ [jmc$jt_system_job_name];
      data_string.lines [1] (data_size, jmc$system_supplied_name_size) := jmc$blank_system_supplied_name;
      data_size := data_size + jmc$system_supplied_name_size;

      data_string.lines [1] (data_size, 4) := 'SFN=';
      data_size := data_size + 4;

      str_size := clp$trimmed_string_size (data_array^ [jmc$jt_system_file_name]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$jt_system_file_name] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 21) := ', OUTPUT_DISPOSITION=';
      data_size := data_size + 21;

      str_size := clp$trimmed_string_size (data_array^ [jmc$jt_output_disposition]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$jt_output_disposition] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 9) := ', REASON=';
      data_size := data_size + 9;

      str_size := clp$trimmed_string_size (data_array^ [jmc$jt_reason]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$jt_reason] (1, str_size);
      data_size := data_size + str_size;

    = jml$print_plot_initiated =

      PUSH data_array: [1 .. jmc$ppi_max_desc_data_fields];
      crack_descriptive_data (header^.statistic_code, descriptive_data, data_array);

      system_job_name := data_array^ [jmc$ppi_system_job_name];
      data_string.lines [1] (data_size, jmc$system_supplied_name_size) := jmc$blank_system_supplied_name;
      data_size := data_size + jmc$system_supplied_name_size;

      data_string.lines [1] (data_size, 4) := 'SFN=';
      data_size := data_size + 4;

      str_size := clp$trimmed_string_size (data_array^ [jmc$ppi_system_file_name]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$ppi_system_file_name] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 11) := ', APP_NAME=';
      data_size := data_size + 11;

      str_size := clp$trimmed_string_size (data_array^ [jmc$ppi_application_name]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$ppi_application_name] (1, str_size);
      data_size := data_size + str_size;

    = jml$print_plot_terminated =

      PUSH data_array: [1 .. jmc$ppt_max_desc_data_fields];
      crack_descriptive_data (header^.statistic_code, descriptive_data, data_array);

      system_job_name := data_array^ [jmc$ppt_system_job_name];
      data_string.lines [1] (data_size, jmc$system_supplied_name_size) := jmc$blank_system_supplied_name;
      data_size := data_size + jmc$system_supplied_name_size;

      data_string.lines [1] (data_size, 4) := 'SFN=';
      data_size := data_size + 4;

      str_size := clp$trimmed_string_size (data_array^ [jmc$ppt_system_file_name]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$ppt_system_file_name] (1, str_size);
      data_size := data_size + str_size;

    = jml$submit_job_executed =

      PUSH data_array: [1 .. jmc$sje_max_desc_data_fields];
      crack_descriptive_data (header^.statistic_code, descriptive_data, data_array);

{ For the submit_job_executed statistic, the system_job_name of the submitting job is in the
{ statistic header and is put out in the header_string. The system_job_name of the submitted job
{ is in the statistic's descriptive data and is put out in the data_string.

      system_job_name := header^.job_name (1, jmc$system_supplied_name_size);
      data_string.lines [1] (data_size, jmc$system_supplied_name_size) := jmc$blank_system_supplied_name;
      data_size := data_size + jmc$system_supplied_name_size;

      data_string.lines [1] (data_size, 4) := 'SJN=';
      data_size := data_size + 4;

      str_size := clp$trimmed_string_size (data_array^ [jmc$sje_system_job_name]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$sje_system_job_name] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 18) := ', JOB_DESTINATION=';
      data_size := data_size + 18;

      str_size := clp$trimmed_string_size (data_array^ [jmc$sje_job_destination]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$sje_job_destination] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 24) := ', JOB_DESTINATION_USAGE=';
      data_size := data_size + 24;

      str_size := clp$trimmed_string_size (data_array^ [jmc$sje_job_destination_usage]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$sje_job_destination_usage] (1,
            str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 6) := ', UJN=';
      data_size := data_size + 6;

      str_size := clp$trimmed_string_size (data_array^ [jmc$sje_user_job_name]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$sje_user_job_name] (1, str_size);

    = jml$print_plot_file_executed =

      PUSH data_array: [1 .. jmc$ppfe_max_desc_data_fields];
      crack_descriptive_data (header^.statistic_code, descriptive_data, data_array);

      system_job_name := data_array^ [jmc$ppfe_system_job_name];
      data_string.lines [1] (data_size, jmc$system_supplied_name_size) := jmc$blank_system_supplied_name;
      data_size := data_size + jmc$system_supplied_name_size;

      data_string.lines [1] (data_size, 4) := 'SFN=';
      data_size := data_size + 4;

      str_size := clp$trimmed_string_size (data_array^ [jmc$ppfe_system_file_name]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$ppfe_system_file_name] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 21) := ', OUTPUT_DESTINATION=';
      data_size := data_size + 21;

      str_size := clp$trimmed_string_size (data_array^ [jmc$ppfe_output_destination]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$ppfe_output_destination] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 27) := ', OUTPUT_DESTINATION_USAGE=';
      data_size := data_size + 27;

      str_size := clp$trimmed_string_size (data_array^ [jmc$ppfe_output_dest_usage]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$ppfe_output_dest_usage] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 17) := ', USER_FILE_NAME=';
      data_size := data_size + 17;

      str_size := clp$trimmed_string_size (data_array^ [jmc$ppfe_user_file_name]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$ppfe_user_file_name] (1, str_size);

    = jml$job_history_message =

{ Use crack_descriptive_data just to get system_job_name. Don't crack message.

      PUSH data_array: [1 .. jmc$jhm_system_job_name];
      crack_descriptive_data (header^.statistic_code, descriptive_data, data_array);

      system_job_name := data_array^ [jmc$jhm_system_job_name];
      data_string.lines [1] (data_size, jmc$system_supplied_name_size) := jmc$blank_system_supplied_name;
      data_size := data_size + jmc$system_supplied_name_size;

      data_string.lines [1] (data_size, 8) := 'MESSAGE=';
      data_size := data_size + 8;

      message_size := header^.descriptive_data_size - jmc$system_supplied_name_size;
      data_string.lines [1] (data_size, message_size) := descriptive_data^
            (jmc$system_supplied_name_size + 1, message_size);

    = jml$non_recovery_of_job =

      PUSH data_array: [1 .. jmc$nroj_max_desc_data_fields];
      crack_descriptive_data (header^.statistic_code, descriptive_data, data_array);

      system_job_name := data_array^ [jmc$nroj_system_job_name];
      data_string.lines [1] (data_size, jmc$system_supplied_name_size) := jmc$blank_system_supplied_name;
      data_size := data_size + jmc$system_supplied_name_size;

      data_string.lines [1] (data_size, 7) := 'REASON=';
      data_size := data_size + 7;

      str_size := clp$trimmed_string_size (data_array^ [jmc$nroj_reason]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$nroj_reason] (1, str_size);

    = jml$change_output_attributes =

      PUSH data_array: [1 .. jmc$coa_max_desc_data_fields];
      crack_descriptive_data (header^.statistic_code, descriptive_data, data_array);

      system_job_name := data_array^ [jmc$coa_system_job_name];
      data_string.line_count := 2;

      data_string.lines [1] (data_size, jmc$system_supplied_name_size) := jmc$blank_system_supplied_name;
      data_size := data_size + jmc$system_supplied_name_size;

      data_string.lines [1] (data_size, 4) := 'SFN=';
      data_size := data_size + 4;

      str_size := clp$trimmed_string_size (data_array^ [jmc$coa_system_file_name]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$coa_system_file_name] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 16) := ', CONTROL_USER=:';
      data_size := data_size + 16;

      str_size := clp$trimmed_string_size (data_array^ [jmc$coa_control_family]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$coa_control_family] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 1) := '.';
      data_size := data_size + 1;

      str_size := clp$trimmed_string_size (data_array^ [jmc$coa_control_user]);
      data_string.lines [1] (data_size, str_size) := data_array^ [jmc$coa_control_user] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [1] (data_size, 1) := ',';

      data_size := 1;

      data_string.lines [2] (data_size, jmc$system_supplied_name_size) := jmc$blank_system_supplied_name;
      data_size := data_size + jmc$system_supplied_name_size;

      data_string.lines [2] (data_size, 19) := 'OUTPUT_DESTINATION=';
      data_size := data_size + 19;

      str_size := clp$trimmed_string_size (data_array^ [jmc$coa_output_destination]);
      data_string.lines [2] (data_size, str_size) := data_array^ [jmc$coa_output_destination] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [2] (data_size, 27) := ', OUTPUT_DESTINATION_USAGE=';
      data_size := data_size + 27;

      str_size := clp$trimmed_string_size (data_array^ [jmc$coa_output_dest_usage]);
      data_string.lines [2] (data_size, str_size) := data_array^ [jmc$coa_output_dest_usage] (1, str_size);
      data_size := data_size + str_size;

      data_string.lines [2] (data_size, 10) := ', STATION=';
      data_size := data_size + 10;

      str_size := clp$trimmed_string_size (data_array^ [jmc$coa_station]);
      data_string.lines [2] (data_size, str_size) := data_array^ [jmc$coa_station] (1, str_size);

    ELSE

{ do nothing - ignore any statistic other than valid job history statistics

    CASEND;

  PROCEND format_descriptive_data;
?? OLDTITLE ??
MODEND jmm$process_job_history;
