?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE : Offline Output Device Support' ??
MODULE jmm$offline_output_support;

{ PURPOSE:
{   This module contains the code for the NOS/VE Management of offline output
{   devices.
{
{ DESIGN:
{   This code executes in ring 3.
{

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc cle$ecc_lexical
*copyc fst$file_reference
*copyc jme$queued_file_conditions
*copyc jmt$error_status_list
*copyc jmt$output_count_range
*copyc jmt$output_descriptor
*copyc jmt$output_device
*copyc ofe$error_codes
*copyc osd$integer_limits
*copyc ost$caller_identifier
*copyc ost$status
*copyc pmt$entry_point_reference
?? POP ??
*copyc amp$get_next
*copyc amp$put_next
*copyc avp$system_administrator
*copyc avp$system_operator
*copyc clp$validate_name
*copyc fsp$close_file
*copyc fsp$open_file
*copyc jmp$acquire_new_output
*copyc jmp$close_output_file
*copyc jmp$new_output_exists
*copyc jmp$open_output_file
*copyc jmp$register_output_application
*copyc jmp$release_output_files
*copyc jmp$set_output_completed
*copyc jmp$set_output_initiated
*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_abnormal
*copyc osp$set_status_condition
?? OLDTITLE ??
?? NEWTITLE := 'jmp$combine_offline_output', EJECT ??
*copy jmh$combine_offline_output

{ DESIGN:
{   Verify that SOU is active with system administration or system operation
{   capability.
{
{   Validate the device name and initialize the combined_file_count and error_file_count.
{
{   Register as the application OSA$COMBINE_OFFLINE_OUTPUT for the output_destination_usage
{   of OFFLINE.
{
{   Open the tape file and verify that it has a device class of magnetic tape.
{
{   While files exist, and less than "number_of_files_to_combine" files have been
{   copied to tape, acquire a new output file.  If the device of the output file
{   does not match the device name specified on the request then set the output file
{   as completed indicating that it could not be disposed of and acquire another new
{   file.
{
{   If the device names match, then open the output file and copy it record by record
{   to the tape.  Close the output file.  If any errors occured, add the file to the
{   error file list and set the output as completed indicating that the file could
{   not be disposed of.  If no errors occured, add the file to the output file list
{   and set the output completed indicating that the file was disposed of.  This removes
{   the file from the output queue.
{
{   When all files are gone, close the tape file and unregister the application
{   OSA$COMBINE_OFFLINE_OUTPUT.

  PROCEDURE [XDCL, #GATE] jmp$combine_offline_output
    (    tape_file_path: fst$file_reference;
         device_name: jmt$output_device;
         number_of_files_to_combine: jmt$output_count_range;
     VAR combined_file_count: jmt$output_count_range;
     VAR combined_file_list: array [1 .. * ] of jmt$output_descriptor;
     VAR error_file_count: jmt$output_count_range;
     VAR error_file_list: jmt$error_status_list;
     VAR status: ost$status);

    CONST
      combine_application_name = 'OSA$COMBINE_OFFLINE_OUTPUT     ',
      combine_destination_usage_name = 'OFFLINE                        ';

    VAR
      attribute_validation_p: ^fst$file_cycle_attributes,
      byte_address: amt$file_byte_address,
      caller_id: ost$caller_identifier,
      combined_file_limit: ost$non_negative_integers,
      error_file_limit: ost$non_negative_integers,
      file_attachment_p: ^fst$attachment_options,
      file_position: amt$file_position,
      local_status: ost$status,
      null_file_access_procedure: pmt$entry_point_reference,
      output_descriptor: jmt$output_descriptor,
      queue_file_identifier: amt$file_identifier,
      queue_file_open: boolean,
      queue_file_password: jmt$queue_file_password,
      scratch_segment_created: boolean,
      tape_file_identifier: amt$file_identifier,
      tape_file_open: boolean,
      transfer_count: amt$transfer_count,
      valid_device_name: jmt$output_device,
      valid_name: boolean,
      work_area_p: ^cell,
      working_storage_area: amt$segment_pointer,
      working_storage_area_size: amt$working_storage_length;

?? NEWTITLE := 'comoo_block_exit_handler', EJECT ??

    PROCEDURE comoo_block_exit_handler
      (    condition: pmt$condition;
           condition_information_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 tape_file_open THEN
        fsp$close_file (tape_file_identifier, ignore_status);
      IFEND;
      IF scratch_segment_created THEN
        mmp$delete_scratch_segment (working_storage_area, ignore_status);
      IFEND;
      IF queue_file_open THEN
        jmp$close_output_file (queue_file_identifier, ignore_status);
      IFEND;
      jmp$release_output_files;
    PROCEND comoo_block_exit_handler;
?? OLDTITLE ??
?? EJECT ??

    status.normal := TRUE;
    local_status.normal := TRUE;
    #CALLER_ID (caller_id);
    tape_file_open := FALSE;
    scratch_segment_created := FALSE;
    queue_file_open := FALSE;
    #SPOIL (tape_file_open, scratch_segment_created, queue_file_open);

    IF NOT (avp$system_administrator () OR avp$system_operator ()) THEN
      osp$set_status_abnormal ('OF', ofe$sou_not_active, 'system_administration or system_operation', status);
      RETURN;
    IFEND;

    clp$validate_name (device_name, valid_device_name, valid_name);
    IF NOT valid_name THEN
      osp$set_status_abnormal ('CL', cle$improper_name, device_name, status);
    IFEND;

    combined_file_count := 0;
    error_file_count := 0;

    combined_file_limit := UPPERBOUND (combined_file_list);
    IF combined_file_limit > number_of_files_to_combine THEN
      combined_file_limit := number_of_files_to_combine;
    IFEND;

    error_file_limit := UPPERBOUND (error_file_list);

    jmp$register_output_application (combine_application_name, combine_destination_usage_name,
          queue_file_password, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ Setup a block exit handler to cleanup should a problem occur.

    osp$establish_block_exit_hndlr (^comoo_block_exit_handler);

    PUSH file_attachment_p: [1 .. 3];
    file_attachment_p^ [1].selector := fsc$access_and_share_modes;
    file_attachment_p^ [1].access_modes.selector := fsc$specific_access_modes;
    file_attachment_p^ [1].access_modes.value := $fst$file_access_options
          [fsc$shorten, fsc$append, fsc$modify, fsc$read];
    file_attachment_p^ [1].share_modes.selector := fsc$specific_share_modes;
    file_attachment_p^ [1].share_modes.value := $fst$file_access_options [];
    file_attachment_p^ [2].selector := fsc$allowed_device_classes;
    file_attachment_p^ [2].allowed_device_classes := $fst$device_classes
          [fsc$magnetic_tape_device];
    file_attachment_p^ [3].selector := fsc$validation_ring;
    file_attachment_p^ [3].validation_ring := caller_id.ring;

    null_file_access_procedure.entry_point := osc$null_name;
    null_file_access_procedure.object_library := '';
    PUSH attribute_validation_p: [1 .. 1];
    attribute_validation_p^ [1].selector := fsc$file_access_procedure_name;
    attribute_validation_p^ [1].file_access_procedure_name := ^null_file_access_procedure;

    fsp$open_file (tape_file_path, amc$record, file_attachment_p, {default_creation_attributes} NIL,
          {mandated_creation_attributes} NIL, attribute_validation_p, {attribute_override} NIL,
          tape_file_identifier, status);
    IF NOT status.normal THEN
      jmp$release_output_files;
      osp$disestablish_cond_handler;
      RETURN;
    IFEND;
    tape_file_open := TRUE;
    #SPOIL (tape_file_open, tape_file_identifier);

    mmp$create_scratch_segment (amc$sequence_pointer, mmc$as_random, working_storage_area, status);
    IF NOT status.normal THEN
      fsp$close_file (tape_file_identifier, {ignore} local_status);
      tape_file_open := FALSE;
      #SPOIL (tape_file_open);
      jmp$release_output_files;
      osp$disestablish_cond_handler;
      RETURN;
    IFEND;
    scratch_segment_created := TRUE;
    #SPOIL (scratch_segment_created, working_storage_area);

    RESET working_storage_area.sequence_pointer;
    NEXT work_area_p IN working_storage_area.sequence_pointer;
    working_storage_area_size := osc$max_segment_length;

    WHILE (combined_file_count < combined_file_limit) AND
          (jmp$new_output_exists (combine_destination_usage_name)) DO
      local_status.normal := TRUE;
      jmp$acquire_new_output (combine_destination_usage_name, output_descriptor, local_status);
      IF local_status.normal AND (output_descriptor.device = valid_device_name) THEN
        jmp$set_output_initiated (combine_destination_usage_name, output_descriptor.system_file_name,
              local_status);
        IF local_status.normal THEN
          jmp$open_output_file (output_descriptor.system_file_name, amc$record,
                combine_destination_usage_name, queue_file_password, queue_file_identifier, local_status);
          IF local_status.normal THEN
            #SPOIL (queue_file_open);
            queue_file_open := TRUE;
            #SPOIL (queue_file_open, queue_file_identifier);

            file_position := amc$boi;
            WHILE local_status.normal AND (file_position <> amc$eoi) DO
              amp$get_next (queue_file_identifier, work_area_p, working_storage_area_size, transfer_count,
                    byte_address, file_position, local_status);
              IF local_status.normal THEN
                amp$put_next (tape_file_identifier, work_area_p, transfer_count, byte_address, local_status);
              IFEND;
            WHILEND;
            IF (file_position = amc$eoi) OR (status.condition = ame$input_after_eoi) THEN
              jmp$close_output_file (queue_file_identifier, {ignore} local_status);
              #SPOIL (queue_file_open);
              queue_file_open := FALSE;
              #SPOIL (queue_file_open);
              jmp$set_output_completed (combine_destination_usage_name, output_descriptor.system_file_name,
                    {completed_successfully} TRUE, {ignore} local_status);
              combined_file_count := combined_file_count + 1;
              combined_file_list [combined_file_count] := output_descriptor;
              local_status.normal := TRUE;
            ELSE
              IF error_file_count < error_file_limit THEN
                error_file_count := error_file_count + 1;
                error_file_list [error_file_count].system_supplied_name := output_descriptor.system_file_name;
                error_file_list [error_file_count].status := local_status;
              IFEND; { error list full
              jmp$close_output_file (queue_file_identifier, {ignore} local_status);
              #SPOIL (queue_file_open);
              queue_file_open := FALSE;
              #SPOIL (queue_file_open);
            IFEND;
          IFEND; { set output initiated
        ELSE
          IF error_file_count < error_file_limit THEN
            error_file_count := error_file_count + 1;
            error_file_list [error_file_count].system_supplied_name := output_descriptor.system_file_name;
            error_file_list [error_file_count].status := local_status;
          IFEND; { error list full
        IFEND; { couldn't open file
      IFEND; { file with correct device acquired
    WHILEND;

    mmp$delete_scratch_segment (working_storage_area, {ignore} local_status);
    #SPOIL (scratch_segment_created);
    scratch_segment_created := FALSE;
    #SPOIL (scratch_segment_created);
    fsp$close_file (tape_file_identifier, status);
    #SPOIL (tape_file_open);
    tape_file_open := FALSE;
    #SPOIL (tape_file_open);
    jmp$release_output_files;
    osp$disestablish_cond_handler;

{ If no files with the specified device were found, report it as an error.

    IF status.normal AND (combined_file_count = 0) AND (error_file_count = 0) THEN
      osp$set_status_condition (jme$no_outputs_were_found, status);
    IFEND;
  PROCEND jmp$combine_offline_output;
?? OLDTITLE ??
MODEND jmm$offline_output_support;
