?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Network Access: Socket Layer External Interface' ??
MODULE nam$sk_socket_layer;
?? RIGHT := 110 ??

{ PURPOSE:
{   This module contains procedures neccesary to support the Program Interface to the NAM/VE
{   Socket Layer.
{ DESIGN:
{   The procedures in this module provide the ability to
{
{        - Create and delete sockets, both UDP and TCP
{        - Send and Receive data over these sockets
{        - Select options for these sockets
{        - Query the attributes of the sockets
{        - Get the local IP addresses
{
{   At present, IPAM is the only user of these procedures. The XDCL'd procedures have been
{   grouped in alphabetical order followed by the internal procedures. The internal procedures
{   are also in alphabetical order. Please refer to the ERS for NAM/VE Socket Layer (A8708)
{   for more information about these procedures.
{   This module contains code that executes in ring 3. It resides on OSF$JOB_TEMPLATE_23D.
{
{ NOTES:
{   The following abbreviations have been used in this module:
{          UDP - User Datagram Protocol
{          TCP - Transmission Control Protocol

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc jmc$system_family
*copyc nac$null_connection_id
*copyc nac$sk_all_ip_addresses
*copyc nac$sk_default_if_timeout
*copyc nac$sk_max_nonblocked_data_size
*copyc nac$sk_unnamed_tcp_application
*copyc nac$sk_unnamed_udp_application
*copyc nae$application_interfaces
*copyc nae$application_management
*copyc nae$sk_socket_layer
*copyc nat$application_name
*copyc nat$application_status
*copyc nat$connection_id
*copyc nat$data_length
*copyc nat$protocol
*copyc nat$sk_host_name
*copyc nat$sk_job_socket
*copyc nat$sk_job_socket_list
*copyc nat$sk_job_socket_status
*copyc nat$sk_listen_queue_limit
*copyc nat$sk_socket_attributes
*copyc nat$sk_socket_events
*copyc nat$sk_socket_identifier
*copyc nat$sk_socket_options
*copyc nat$sk_socket_status
*copyc nat$sk_socket_type
*copyc nat$wait_time
*copyc nlc$sk_min_assigned_port
*copyc nlc$sk_max_assigned_port
*copyc nlc$sk_max_priv_reserved_port
*copyc nlc$udp_null_global_socket_id
*copyc nlt$cl_connection
*copyc nlt$cl_connection_layer_templat
*copyc nlt$cl_layer_name
*copyc nlt$tcp_open_port
*copyc nlt$tcp_ports
*copyc nlt$tcp_received_data
*copyc nlt$tcp_receiver_task
*copyc nlt$tcp_sender_task
*copyc nlt$tcp_socket_type
*copyc nlt$udp_global_socket
*copyc nlt$udp_global_socket_id
*copyc nlt$udp_open_port
*copyc nlt$udp_ports
*copyc osd$virtual_address
*copyc ost$caller_identifier
*copyc ost$name
*copyc ost$signature_lock_status
*copyc ost$status
*copyc ost$user_identification
?? POP ??
*copyc nap$condition_handler_trace
*copyc nap$get_tcpip_attributes
*copyc nap$namve_system_error
*copyc nap$validate_user
*copyc nlp$al_get_data_length
*copyc nlp$bm_deliver_message
*copyc nlp$cc_receive_data
*copyc nlp$cc_report_undelivered_data
*copyc nlp$cl_activate_layer
*copyc nlp$cl_activate_receiver
*copyc nlp$cl_activate_sender
*copyc nlp$cl_clear_exclusive_access
*copyc nlp$cl_create_connection
*copyc nlp$cl_deactivate_layer
*copyc nlp$cl_deactivate_receiver
*copyc nlp$cl_deactivate_sender
*copyc nlp$cl_get_exclusive_via_cid
*copyc nlp$cl_get_layer_connection
*copyc nlp$cl_release_exclusive_access
*copyc nlp$osi_get_outbound_capacity
*copyc nlp$sk_accept_socket_offer
*copyc nlp$sk_add_job_socket
*copyc nlp$sk_clear_job_socket_lock
*copyc nlp$sk_delete_job_socket
*copyc nlp$sk_free_socket_id
*copyc nlp$sk_get_socket_id
*copyc nlp$sk_lock_job_socket
*copyc nlp$sk_offer_socket
*copyc nlp$sk_tcp_accept_socket
*copyc nlp$sk_tcp_activate_listen
*copyc nlp$sk_tcp_cancel_socket_offer
*copyc nlp$sk_tcp_check_accept_socket
*copyc nlp$sk_tcp_close_socket
*copyc nlp$sk_tcp_deactivate_layer
*copyc nlp$sk_tcp_get_rec_task_entry
*copyc nlp$sk_tcp_get_listen_addresses
*copyc nlp$sk_tcp_get_send_task_entry
*copyc nlp$sk_tcp_get_socket_status
*copyc nlp$sk_tcp_offer_socket
*copyc nlp$sk_tcp_ret_rec_data_entry
*copyc nlp$sk_tcp_ret_rec_task_entry
*copyc nlp$sk_tcp_ret_send_task_entry
*copyc nlp$sk_tcp_send_data
*copyc nlp$sk_tcp_set_socket_options
*copyc nlp$sk_tcp_terminate_listen
*copyc nlp$sk_unlock_job_socket
*copyc nlp$sk_update_bound_address
*copyc nlp$sk_update_connect_socket
*copyc nlp$sk_update_job_socket
*copyc nlp$sk_update_job_socket_status
*copyc nlp$sk_update_listen_socket
*copyc nlp$sk_update_socket_options
*copyc nlp$tm_get_local_addresses
*copyc nlp$tcp_connect_socket
*copyc nlp$tcp_release_socket
*copyc nlp$tcpip_decrement_appl_access
*copyc nlp$tcpip_increment_appl_access
*copyc nlp$tm_select_by_local_tcp_addr
*copyc nlp$tm_tcp_select_device
*copyc nlp$udp_bind_socket
*copyc nlp$udp_cancel_socket_offer
*copyc nlp$udp_close_socket
*copyc nlp$udp_create_global_socket
*copyc nlp$udp_delete_global_socket
*copyc nlp$udp_free_exclusive_access
*copyc nlp$udp_get_bound_addresses
*copyc nlp$udp_get_socket_status
*copyc nlp$udp_offer_socket
*copyc nlp$udp_receive_data
*copyc nlp$udp_send_data
*copyc nlp$udp_set_socket_options
*copyc osp$append_status_integer
*copyc osp$clear_job_signature_lock
*copyc osp$disestablish_cond_handler
*copyc osp$establish_block_exit_hndlr
*copyc osp$establish_condition_handler
*copyc osp$pop_inhibit_job_recovery
*copyc osp$push_inhibit_job_recovery
*copyc osp$set_job_signature_lock
*copyc osp$set_status_abnormal
*copyc osp$set_status_condition
*copyc osp$set_status_from_condition
*copyc pmp$continue_to_cause
*copyc pmp$get_executing_task_gtid
*copyc pmp$get_job_names
*copyc pmp$get_user_identification
*copyc pmp$ready_task
*copyc pmp$wait
*copyc syp$cycle
*copyc nav$network_paged_heap
*copyc nav$sk_job_socket_assignment
*copyc nav$sk_job_socket_list
*copyc nav$sk_socket_layer_active
*copyc nlv$bm_null_message_id
*copyc nlv$tcp_ports
*copyc nlv$tm_host
*copyc nlv$udp_ports
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??
*copyc nlc$udp_null_global_socket_id

  CONST
    loopback_address = (127 * 256 * 256 * 256) + 1; { 127.0.0.1 in standard IP adddress notation.

?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$sk_accept_socket', EJECT ??
*copy nah$sk_accept_socket

  PROCEDURE [XDCL, #GATE] nap$sk_accept_socket
    (    listen_socket_id: nat$sk_socket_identifier;
     VAR accept_socket_id: nat$sk_socket_identifier;
     VAR source_socket: nat$sk_socket_address;
     VAR status: ost$status);

?? NEWTITLE := 'terminate_accept_socket', EJECT ??

    PROCEDURE terminate_accept_socket
      (    condition: pmt$condition;
           condition_descriptor: ^pmt$condition_information;
           save_area: ^ost$stack_frame_save_area;
       VAR handler_status: ost$status);

      nap$condition_handler_trace (condition, save_area);
      IF socket_locked THEN
        nlp$sk_clear_job_socket_lock (accept_socket_id);
        nlp$sk_free_socket_id (accept_socket_id);
      IFEND;
      osp$pop_inhibit_job_recovery;

    PROCEND terminate_accept_socket;
?? OLDTITLE, EJECT ??

    VAR
      caller_id: ost$caller_identifier,
      connection_id: nat$connection_id,
      current_task_id: ost$global_task_id,
      listen_socket: ^nat$sk_job_socket,
      local_ip_address: nat$sk_ip_address,
      nil_socket: ^nat$sk_job_socket,
      saved_job_socket: nat$sk_job_socket,
      socket_locked: boolean,
      wait_time: nat$wait_time;

    status.normal := TRUE;
    socket_locked := FALSE;
    #SPOIL (socket_locked);
    pmp$get_executing_task_gtid (current_task_id);
    osp$push_inhibit_job_recovery;
    IF listen_socket_id > 0 THEN
      nlp$sk_lock_job_socket (listen_socket_id, listen_socket);
      IF listen_socket <> NIL THEN
        #CALLER_ID (caller_id);
        IF caller_id.ring <= listen_socket^.ring THEN
          IF listen_socket^.socket_type = nac$sk_tcp_socket THEN
            IF listen_socket^.status = nac$sk_socket_open THEN
              IF listen_socket^.tcp_socket_type = nlc$tcp_listen_socket THEN
                nlp$sk_get_socket_id (accept_socket_id, status);
                IF status.normal THEN
                  #SPOIL (accept_socket_id);
                  osp$establish_block_exit_hndlr (^terminate_accept_socket);
                  nlp$sk_lock_job_socket (accept_socket_id, nil_socket);
                  socket_locked := TRUE;
                  #SPOIL (socket_locked);
                  saved_job_socket := listen_socket^;
                  nlp$sk_unlock_job_socket (listen_socket_id);
                  IF (saved_job_socket.interface_mode = nac$sk_blocking_mode) THEN
                    wait_time := saved_job_socket.interface_timeout;
                  ELSE
                    wait_time := 0;
                  IFEND;

                  nlp$sk_tcp_accept_socket (saved_job_socket.port, saved_job_socket.bound_address, TRUE
                        {graceful_close} , nac$sk_tcp_random_traffic, wait_time, connection_id, source_socket,
                        local_ip_address, status);
                  IF status.normal THEN

{ Setup the job socket attributes for the accepted socket.

                    saved_job_socket.identifier := accept_socket_id;
                    IF connection_id <> nac$null_connection_id THEN
                      saved_job_socket.status := nac$sk_socket_open;
                    ELSE
                      saved_job_socket.status := nac$sk_socket_closed_via_peer;
                    IFEND;
                    saved_job_socket.time_stamp := #FREE_RUNNING_CLOCK (0);
                    saved_job_socket.bound_address := local_ip_address;
                    saved_job_socket.owner := current_task_id;
                    saved_job_socket.interface_mode := nac$sk_blocking_mode;
                    saved_job_socket.interface_timeout := nac$sk_default_if_timeout;
                    saved_job_socket.traffic_pattern := nac$sk_tcp_random_traffic;
                    saved_job_socket.connection_id := connection_id;
                    saved_job_socket.local_ip_address := local_ip_address;
                    saved_job_socket.tcp_socket_type := nlc$tcp_accept_socket;
                    saved_job_socket.reuse_address := FALSE;
                    saved_job_socket.graceful_close := TRUE;
                    saved_job_socket.selection_criteria.ip_address := nac$sk_all_ip_addresses;
                    saved_job_socket.selection_criteria.port := 0;
                    nlp$sk_add_job_socket (accept_socket_id, saved_job_socket);
                    nlp$sk_unlock_job_socket (accept_socket_id);
                    socket_locked := FALSE;
                    #SPOIL (socket_locked);
                  ELSE
                    nlp$sk_unlock_job_socket (accept_socket_id);
                    socket_locked := FALSE;
                    #SPOIL (socket_locked);
                    nlp$sk_free_socket_id (accept_socket_id);
                    IF status.condition = nae$sk_socket_terminated THEN
                      osp$append_status_integer (osc$status_parameter_delimiter, listen_socket_id, 10, TRUE,
                            status);
                      nlp$sk_lock_job_socket (listen_socket_id, listen_socket);
                      IF (listen_socket <> NIL) AND (listen_socket^.time_stamp = saved_job_socket.time_stamp)
                            THEN
                        nlp$sk_update_job_socket_status (listen_socket_id, nac$sk_socket_terminated);
                      IFEND;
                      nlp$sk_unlock_job_socket (listen_socket_id);
                    IFEND;
                  IFEND;
                  osp$disestablish_cond_handler;
                ELSE { Max socket limit reached
                  nlp$sk_unlock_job_socket (listen_socket_id);
                IFEND;
              ELSE { Listen not done
                nlp$sk_unlock_job_socket (listen_socket_id);
                osp$set_status_condition (nae$sk_listen_not_done, status);
                osp$append_status_integer (osc$status_parameter_delimiter, listen_socket_id, 10, TRUE,
                      status);
              IFEND;
            ELSE
              nlp$sk_unlock_job_socket (listen_socket_id);
              IF listen_socket^.status = nac$sk_socket_terminated THEN
                osp$set_status_condition (nae$sk_socket_terminated, status);
              ELSEIF listen_socket^.status = nac$sk_job_recovery THEN
                osp$set_status_condition (nae$sk_job_recovery, status);
              IFEND;
              osp$append_status_integer (osc$status_parameter_delimiter, listen_socket_id, 10, TRUE, status);
            IFEND;
          ELSE { Incorrect socket type
            nlp$sk_unlock_job_socket (listen_socket_id);
            osp$set_status_abnormal (nac$status_id, nae$sk_incorrect_socket_type, 'ACCEPT SOCKET', status);
          IFEND;
        ELSE { Invalid user
          nlp$sk_unlock_job_socket (listen_socket_id);
          osp$set_status_abnormal (nac$status_id, nae$sk_invalid_user, 'NAP$SK_ACCEPT_SOCKET', status);
        IFEND;
      ELSE { Unknown socket
        nlp$sk_unlock_job_socket (listen_socket_id);
        osp$set_status_condition (nae$sk_unknown_socket, status);
        osp$append_status_integer (osc$status_parameter_delimiter, listen_socket_id, 10, TRUE, status);
      IFEND;
    ELSE { listen_socket_id = 0
      osp$set_status_condition (nae$sk_unknown_socket, status);
      osp$append_status_integer (osc$status_parameter_delimiter, listen_socket_id, 10, TRUE, status);
    IFEND;
    osp$pop_inhibit_job_recovery;

  PROCEND nap$sk_accept_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$sk_accept_socket_offer', EJECT ??
*copy nah$sk_accept_socket_offer

  PROCEDURE [XDCL, #GATE] nap$sk_accept_socket_offer
    (    source_job: jmt$system_supplied_name;
         wait_time: nat$wait_time;
     VAR socket_id: nat$sk_socket_identifier;
     VAR status: ost$status);

?? NEWTITLE := 'terminate_accept_socket_offer', EJECT ??

    PROCEDURE terminate_accept_socket_offer
      (    condition: pmt$condition;
           condition_descriptor: ^pmt$condition_information;
           save_area: ^ost$stack_frame_save_area;
       VAR handler_status: ost$status);

      nap$condition_handler_trace (condition, save_area);
      IF socket_locked THEN
        nlp$sk_clear_job_socket_lock (socket_id);
        nlp$sk_free_socket_id (socket_id);
      IFEND;
      osp$pop_inhibit_job_recovery;

    PROCEND terminate_accept_socket_offer;
?? OLDTITLE, EJECT ??

    VAR
      application: nat$application_name,
      bound_address: nat$sk_ip_address,
      capability: ost$name,
      connection_id: nat$connection_id,
      current_task_id: ost$global_task_id,
      global_socket_id: nlt$udp_global_socket_id,
      job_socket: nat$sk_job_socket,
      old_job_socket: ^nat$sk_job_socket,
      port: nat$sk_port_number,
      ring: ost$ring,
      socket_locked: boolean,
      socket_type: nat$sk_socket_type,
      tcp_socket_type: nlt$tcp_socket_type,
      time_stamp: ost$free_running_clock,
      traffic_pattern: nat$sk_traffic_pattern;

    status.normal := TRUE;
    pmp$get_executing_task_gtid (current_task_id);
    osp$push_inhibit_job_recovery;
    socket_locked := FALSE;
    #SPOIL (socket_locked);
    nlp$sk_get_socket_id (socket_id, status);
    IF status.normal THEN
      osp$establish_block_exit_hndlr (^terminate_accept_socket_offer);
      nlp$sk_lock_job_socket (socket_id, old_job_socket);
      socket_locked := TRUE;
      #SPOIL (socket_locked);
      #SPOIL (socket_id);
      IF old_job_socket = NIL THEN
        time_stamp := #FREE_RUNNING_CLOCK (0);
        nlp$sk_accept_socket_offer (source_job, socket_id, time_stamp, wait_time, socket_type,
              global_socket_id, connection_id, tcp_socket_type, bound_address, port, traffic_pattern,
              application, ring, capability, status);
        IF status.normal THEN

{ Set up the job socket entry.
{ Store all fields in the job socket entry.
{ Initialize all socket attributes to default values.
{ Initialize status to open.

          job_socket.identifier := socket_id;
          job_socket.time_stamp := time_stamp;
          job_socket.status := nac$sk_socket_open;
          job_socket.socket_type := socket_type;
          job_socket.application := application;
          job_socket.capability := capability;
          job_socket.ring := ring;
          job_socket.system_privilege := FALSE;
          job_socket.port := port;

{ Initialize the socket attributes to the default values except for
{ the bound address and the traffic pattern. The bound address and
{ traffic pattern are set to the values assigned by the source job.

          job_socket.bound_address := bound_address;
          job_socket.owner := current_task_id;
          job_socket.interface_mode := nac$sk_blocking_mode;
          job_socket.interface_timeout := nac$sk_default_if_timeout;
          job_socket.traffic_pattern := traffic_pattern;
          IF socket_type = nac$sk_udp_socket THEN
            job_socket.global_socket_id := global_socket_id;
            job_socket.checksum := TRUE;
            job_socket.local_ip_address_enabled := FALSE;
            job_socket.user_cache_enabled := TRUE;
            job_socket.broadcast_enabled := FALSE;
          ELSEIF socket_type = nac$sk_tcp_socket THEN
            job_socket.connection_id := connection_id;
            job_socket.tcp_socket_type := tcp_socket_type;
            job_socket.reuse_address := FALSE;
            job_socket.graceful_close := TRUE;
            job_socket.selection_criteria.ip_address := nac$sk_all_ip_addresses;
            job_socket.selection_criteria.port := 0;
          ELSE { unknown socket type
            nap$namve_system_error ({Recoverable_error=} TRUE, 'Accepted an unknown socket type.', NIL);
            osp$set_status_condition (nae$sk_unknown_socket_type, status);
          IFEND;
          IF status.normal THEN
            nlp$sk_add_job_socket (socket_id, job_socket);
          ELSE
            nlp$sk_free_socket_id (socket_id);
          IFEND;
        ELSE { No socket offer
          nlp$sk_free_socket_id (socket_id);
        IFEND;
      ELSE { job_socket <> NIL
        nlp$sk_free_socket_id (socket_id);
        nap$namve_system_error ({Recoverable_error=} TRUE, 'Socket layer assigned a non nil socket.', NIL);
        osp$set_status_abnormal (nac$status_id, nae$sk_internal_error, 'ACCEPT SOCKET OFFER', status);
      IFEND;
      nlp$sk_unlock_job_socket (socket_id);
      osp$disestablish_cond_handler;
    IFEND;
    osp$pop_inhibit_job_recovery;

  PROCEND nap$sk_accept_socket_offer;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$sk_bind_socket', EJECT ??
*copy nah$sk_bind_socket

  PROCEDURE [XDCL, #GATE] nap$sk_bind_socket
    (    socket_id: nat$sk_socket_identifier;
         port: nat$sk_port_number;
         ip_address: nat$sk_ip_address;
     VAR status: ost$status);

    VAR
      caller_id: ost$caller_identifier,
      identification: ost$user_identification,
      job_socket: ^nat$sk_job_socket;

    status.normal := TRUE;
    osp$push_inhibit_job_recovery;
    IF socket_id > 0 THEN
      nlp$sk_lock_job_socket (socket_id, job_socket);
      IF (job_socket <> NIL) THEN
        #CALLER_ID (caller_id);
        IF caller_id.ring <= job_socket^.ring THEN
          IF job_socket^.status = nac$sk_socket_unbound THEN
            IF (port > 0) AND (port <= nlc$sk_max_priv_reserved_port) THEN

{ The user must be in the $system user and family or executing in ring 6 in order to open
{ privileged ports.

              pmp$get_user_identification (identification, status);
              IF status.normal THEN

{             IF ((identification.user <> jmc$system_user) OR
{               (identification.family <> jmc$system_family)) AND
{               (caller_id.ring > osc$sj_ring_3) THEN
{               osp$set_status_abnormal (nac$status_id, nae$sk_invalid_user, 'PRIVILEGED PORT', status);
{             IFEND;

              IFEND;
            IFEND;
            IF status.normal THEN
              IF job_socket^.socket_type = nac$sk_udp_socket THEN
                bind_udp_socket (job_socket, port, ip_address, status);
                IF (NOT status.normal) AND (status.condition = nae$sk_socket_terminated) THEN
                  nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                  osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                IFEND;
              ELSEIF job_socket^.socket_type = nac$sk_tcp_socket THEN
                bind_tcp_socket (job_socket, port, ip_address, status);
              IFEND;
            IFEND;
          ELSEIF job_socket^.status = nac$sk_job_recovery THEN
            osp$set_status_condition (nae$sk_job_recovery, status);
            osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
          ELSE { socket already bound
            osp$set_status_condition (nae$sk_socket_already_bound, status);
            osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
          IFEND;
        ELSE { Invalid user
          osp$set_status_abnormal (nac$status_id, nae$sk_invalid_user, 'NAP$SK_BIND_SOCKET', status);
        IFEND;
      ELSE { Unknown socket
        osp$set_status_condition (nae$sk_unknown_socket, status);
        osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
      IFEND;

      nlp$sk_unlock_job_socket (socket_id);
    ELSE { socket_id = 0
      osp$set_status_condition (nae$sk_unknown_socket, status);
      osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
    IFEND;
    osp$pop_inhibit_job_recovery;

  PROCEND nap$sk_bind_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$sk_close_socket', EJECT ??
*copy nah$sk_close_socket

  PROCEDURE [XDCL, #GATE] nap$sk_close_socket
    (    socket_id: nat$sk_socket_identifier;
     VAR status: ost$status);

    VAR
      caller_id: ost$caller_identifier,
      current_task_id: ost$global_task_id,
      job_socket: ^nat$sk_job_socket;

    status.normal := TRUE;
    pmp$get_executing_task_gtid (current_task_id);
    osp$push_inhibit_job_recovery;
    IF socket_id > 0 THEN
      nlp$sk_lock_job_socket (socket_id, job_socket);
      IF (job_socket <> NIL) THEN
        #CALLER_ID (caller_id);
        IF caller_id.ring <= job_socket^.ring THEN
          IF job_socket^.owner = current_task_id THEN
            close_socket (job_socket, status);
          ELSE {Current task not the owner

{       Ignore it.

          IFEND;
        ELSE { Invalid user
          osp$set_status_abnormal (nac$status_id, nae$sk_invalid_user, 'NAP$SK_CLOSE_SOCKET', status);
        IFEND;
      ELSE { job_socket = NIL
        osp$set_status_condition (nae$sk_unknown_socket, status);
        osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
      IFEND;
      nlp$sk_unlock_job_socket (socket_id);
    ELSE { socket_id = 0
      osp$set_status_condition (nae$sk_unknown_socket, status);
      osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
    IFEND;
    osp$pop_inhibit_job_recovery;

  PROCEND nap$sk_close_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$sk_connect_socket', EJECT ??
*copy nah$sk_connect_socket

  PROCEDURE [XDCL, #GATE] nap$sk_connect_socket
    (    socket_id: nat$sk_socket_identifier;
         destination_socket: nat$sk_socket_address;
     VAR status: ost$status);

?? NEWTITLE := 'terminate_connect', EJECT ??

    PROCEDURE terminate_connect
      (    condition: pmt$condition;
           condition_descriptor: ^pmt$condition_information;
           save_area: ^ost$stack_frame_save_area;
       VAR handler_status: ost$status);

      nap$condition_handler_trace (condition, save_area);
      nlp$cl_get_exclusive_via_cid (connection_id, connection_exists, cl_connection);
      IF cl_connection <> NIL THEN
        nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active, tcp_connection);
        IF layer_active THEN
          IF (tcp_connection^.state <> nlc$tcp_conn_closed) AND
                (tcp_connection^.state <> nlc$tcp_conn_terminated) THEN

{ Send a disconnect to the peer.

            nlp$tcp_release_socket (cl_connection, {ignore} local_status);
            tcp_connection^.state := nlc$tcp_conn_closed;
            nlp$cl_deactivate_layer (nlc$tcp_interface, cl_connection);
            decrement_access_count := TRUE;
          ELSEIF tcp_connection^.state = nlc$tcp_conn_closed THEN
            decrement_access_count := TRUE;
          IFEND;
        IFEND;
        nlp$cl_release_exclusive_access (cl_connection);
        IF decrement_access_count THEN
          nlp$tcpip_decrement_appl_access (job_socket^.application, nlc$udp_null_global_socket_id,
                connection_id, {ignore} local_status);
        IFEND;

      ELSE { Socket may be gone because of job recovery.

      IFEND;
      IF job_socket^.status = nac$sk_socket_unbound THEN
        close_tcp_port (nac$sk_all_ip_addresses, port, FALSE);
      IFEND;
      nlp$sk_clear_job_socket_lock (socket_id);
      osp$pop_inhibit_job_recovery;

    PROCEND terminate_connect;
?? OLDTITLE, EJECT ??

    VAR
      caller_id: ost$caller_identifier,
      cl_connection: ^nlt$cl_connection,
      connection_exists: boolean,
      connection_id: nat$connection_id,
      current_time: ost$free_running_clock,
      decrement_access_count: boolean,
      destination_address: nat$sk_socket_address,
      end_time: integer,
      job_socket: ^nat$sk_job_socket,
      layer_active: boolean,
      local_ip_address: nat$sk_ip_address,
      local_status: ost$status,
      port: nat$sk_port_number,
      remaining_time: integer,
      selected_device: nlt$device_identifier,
      source_socket: nat$sk_socket_address,
      tcp_connection: ^nlt$tcp_socket_layer;

    status.normal := TRUE;
    decrement_access_count := FALSE;
    #SPOIL (decrement_access_count);
    osp$push_inhibit_job_recovery;
    IF socket_id > 0 THEN
      nlp$sk_lock_job_socket (socket_id, job_socket);
      #SPOIL (job_socket);
      IF job_socket <> NIL THEN
        #CALLER_ID (caller_id);
        IF caller_id.ring <= job_socket^.ring THEN
          IF job_socket^.socket_type = nac$sk_tcp_socket THEN
            IF job_socket^.status = nac$sk_socket_unbound THEN
              open_tcp_port (job_socket^.bound_address, port, status);
              #SPOIL (port);
            ELSEIF job_socket^.status = nac$sk_socket_open THEN
              port := job_socket^.port;
              #SPOIL (port);
              IF job_socket^.connection_id <> nac$null_connection_id THEN
                osp$set_status_condition (nae$sk_socket_already_connected, status);
                osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, FALSE, status);
              ELSEIF job_socket^.tcp_socket_type = nlc$tcp_listen_socket THEN
                osp$set_status_condition (nae$sk_listen_already_active, status);
                osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, FALSE, status);
              IFEND;
            ELSEIF job_socket^.status = nac$sk_socket_terminated THEN
              osp$set_status_condition (nae$sk_socket_terminated, status);
              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, FALSE, status);
            ELSEIF job_socket^.status = nac$sk_socket_disconnected THEN
              osp$set_status_condition (nae$sk_socket_disconnected, status);
              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, FALSE, status);
            ELSEIF job_socket^.status = nac$sk_socket_closed_via_peer THEN
              osp$set_status_condition (nae$sk_socket_closed_via_peer, status);
              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, FALSE, status);
            ELSEIF job_socket^.status = nac$sk_job_recovery THEN
              osp$set_status_condition (nae$sk_job_recovery, status);
              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, FALSE, status);
            IFEND;

            IF status.normal THEN

{ Select the device. Note that the loopback address is detected here and affects device selection.

              IF job_socket^.bound_address = nac$sk_all_ip_addresses THEN
                IF destination_socket.ip_address <> loopback_address THEN
                  destination_address := destination_socket;
                  nlp$tm_tcp_select_device (destination_socket.ip_address, selected_device, local_ip_address,
                        status);
                ELSE { loopback address ... get a valid local TCP address for destination.
                  IF nlv$tm_device_configuration^.tcp.count > 0 THEN
                    selected_device := nlv$tm_device_configuration^.tcp.identifier;
                    local_ip_address := nlv$tm_device_configuration^.list [selected_device].
                          local_device_address.full;
                    destination_address.ip_address := local_ip_address;
                    destination_address.port := destination_socket.port;
                  ELSE
                    osp$set_status_condition (nae$tm_no_tcp_device_available, status);
                  IFEND;
                IFEND;
              ELSE { bound to specific address
                local_ip_address := job_socket^.bound_address;
                nlp$tm_select_by_local_tcp_addr (local_ip_address, selected_device, status);
                IF destination_socket.ip_address <> loopback_address THEN
                  destination_address := destination_socket;
                ELSE { loopback address ... use address of device in use.
                  destination_address.ip_address := local_ip_address;
                  destination_address.port := destination_socket.port;
                IFEND;
              IFEND;

              IF status.normal THEN

{ Establish the channel connection to the destination address via the selected device.

                source_socket.ip_address := local_ip_address;
                source_socket.port := port;
                nlp$cl_create_connection (nlc$tcp_interface, cl_connection);
                IF cl_connection <> NIL THEN
                  nlp$tcpip_increment_appl_access (job_socket^.application, {assigned} TRUE,
                        nlc$udp_null_global_socket_id, cl_connection^.identifier, status);
                  IF status.normal THEN
                    nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, {ignore} layer_active,
                          tcp_connection);
                    IF tcp_connection <> NIL THEN
                      tcp_connection^.device_id := selected_device;
                      tcp_connection^.disconnect_reason := 0;
                      tcp_connection^.user_initiated_close := FALSE;
                      tcp_connection^.socket_id := socket_id;
                      tcp_connection^.socket_type := nlc$tcp_connect_socket;
                      tcp_connection^.inventory_report := 0;
                      tcp_connection^.send_queue := NIL;
                      tcp_connection^.receive_queue := NIL;
                      tcp_connection^.received_data := NIL;
                      tcp_connection^.available_receiver_pool := NIL;
                      tcp_connection^.available_sender_pool := NIL;
                      tcp_connection^.available_data_pool := NIL;
                      tcp_connection^.source_socket := source_socket;
                      tcp_connection^.destination_socket := destination_address;
                      tcp_connection^.waiting_task_id.index := 0;
                      nlp$tcp_connect_socket (cl_connection, source_socket, destination_address,
                            job_socket^.graceful_close, job_socket^.traffic_pattern, nlc$cc_normal_class,
                            selected_device, status);
                      IF status.normal THEN
                        nlp$cl_activate_layer (nlc$tcp_interface, cl_connection);
                        tcp_connection^.state := nlc$tcp_conn_await_confirm;
                        pmp$get_executing_task_gtid (tcp_connection^.waiting_task_id);
                        connection_id := cl_connection^.identifier;
                        #SPOIL (connection_id);
                      ELSE
                        decrement_access_count := TRUE;
                      IFEND;
                    ELSE { tcp_connection = NIL
                      decrement_access_count := TRUE;
                      nap$namve_system_error ({Recoverable_error=} TRUE, 'TCP Socket layer NIL.', NIL);
                      osp$set_status_abnormal (nac$status_id, nae$sk_internal_error, 'CONNECT SOCKET',
                            status);
                    IFEND;
                  ELSEIF status.condition = nae$maximum_sockets_exceeded THEN
                    osp$set_status_condition (nae$sk_max_sockets_limit, status);
                  IFEND;
                  nlp$cl_release_exclusive_access (cl_connection);
                ELSE { Resource constraint
                  osp$set_status_abnormal (nac$status_id, nae$sk_insufficient_resources, 'CONNECT SOCKET',
                        status);
                IFEND;
              IFEND;

              IF status.normal THEN
                #SPOIL (connection_id);
                #SPOIL (job_socket);
                #SPOIL (decrement_access_count);
                #SPOIL (port);
                end_time := #FREE_RUNNING_CLOCK (0) + job_socket^.interface_timeout * 1000;
                remaining_time := job_socket^.interface_timeout;
                osp$establish_block_exit_hndlr (^terminate_connect);

              /wait_for_response/
                REPEAT
                  pmp$wait (remaining_time, 0);
                  current_time := #FREE_RUNNING_CLOCK (0);
                  IF current_time < end_time THEN
                    remaining_time := (end_time - current_time) DIV 1000;
                  ELSE
                    remaining_time := 0;
                  IFEND;

{ Check if the connection has been accepted.

                  nlp$cl_get_exclusive_via_cid (connection_id, connection_exists, cl_connection);
                  IF cl_connection <> NIL THEN
                    nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active,
                          tcp_connection);
                    IF layer_active THEN
                      IF (tcp_connection^.state = nlc$tcp_conn_open) OR
                            (tcp_connection^.state = nlc$tcp_conn_closing) THEN
                        tcp_connection^.waiting_task_id.index := 0;
                        nlp$cl_release_exclusive_access (cl_connection);
                        IF job_socket^.status = nac$sk_socket_unbound THEN
                          nlp$sk_update_job_socket (socket_id, port, nac$sk_all_ip_addresses,
                                nac$sk_socket_open);
                        IFEND;
                        nlp$sk_update_connect_socket (socket_id, connection_id, local_ip_address);
                        EXIT /wait_for_response/; {----->
                      ELSEIF tcp_connection^.state = nlc$tcp_conn_closed THEN

{ A disconnect was received from the peer.

                        nlp$cl_deactivate_layer (nlc$tcp_interface, cl_connection);
                        CASE tcp_connection^.disconnect_reason OF
                        = nlc$tcpaa_ri_address_in_use =
                          osp$set_status_condition (nae$sk_address_in_use, status);
                        = nlc$tcpaa_ri_user_termination =
                          osp$set_status_condition (nae$sk_socket_closed_via_peer, status);
                        ELSE
                          osp$set_status_condition (nae$sk_socket_disconnected, status);
                        CASEND;
                        osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                              status);
                        decrement_access_count := TRUE;
                      ELSEIF remaining_time = 0 THEN

{ Send a disconnect to the peer.

                        nlp$tcp_release_socket (cl_connection, {ignore} local_status);
                        tcp_connection^.state := nlc$tcp_conn_closed;
                        nlp$cl_deactivate_layer (nlc$tcp_interface, cl_connection);
                        osp$set_status_condition (nae$sk_interface_timeout, status);
                        osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                              status);
                        decrement_access_count := TRUE;
                      IFEND;
                    ELSE { Layer inactive

{ The connection was terminated via application management.

                      osp$set_status_condition (nae$sk_socket_terminated, status);
                      osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                    IFEND;
                    nlp$cl_release_exclusive_access (cl_connection);
                  ELSE { cl_connection = NIL

{ The connection was terminated via application management.

                    osp$set_status_condition (nae$sk_socket_terminated, status);
                    osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                  IFEND;
                UNTIL (NOT status.normal) OR (remaining_time = 0);
                osp$disestablish_cond_handler;
              IFEND;

              IF decrement_access_count THEN
                nlp$tcpip_decrement_appl_access (job_socket^.application, nlc$udp_null_global_socket_id,
                      connection_id, {ignore} local_status);
              IFEND;
              IF (NOT status.normal) AND (job_socket^.status = nac$sk_socket_unbound) THEN
                close_tcp_port (nac$sk_all_ip_addresses, port, FALSE);
              IFEND;
              IF (NOT status.normal) AND (status.condition = nae$sk_socket_terminated) THEN
                nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
              IFEND;
            IFEND;
          ELSE { Incorrect socket type
            osp$set_status_abnormal (nac$status_id, nae$sk_incorrect_socket_type, 'CONNECT SOCKET', status);
          IFEND;
        ELSE { Invalid user
          osp$set_status_abnormal (nac$status_id, nae$sk_invalid_user, 'NAP$SK_CONNECT_SOCKET', status);
        IFEND;
      ELSE { Unknown socket
        osp$set_status_condition (nae$sk_unknown_socket, status);
        osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
      IFEND;
      nlp$sk_unlock_job_socket (socket_id);
    ELSE { socket_id = 0
      osp$set_status_condition (nae$sk_unknown_socket, status);
      osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
    IFEND;
    osp$pop_inhibit_job_recovery;

  PROCEND nap$sk_connect_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$sk_get_host_name', EJECT ??
*copy nah$sk_get_host_name

  PROCEDURE [XDCL, #GATE] nap$sk_get_host_name
    (VAR host_name: nat$sk_host_name;
     VAR status: ost$status);

    status.normal := TRUE;
    IF nav$sk_socket_layer_active THEN
      host_name.length := nlv$tm_host.name_length;
      host_name.value := nlv$tm_host.name;
    ELSE
      osp$set_status_condition (nae$sk_tcpip_host_not_defined, status);
    IFEND;

  PROCEND nap$sk_get_host_name;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$sk_get_local_addresses', EJECT ??
*copy nah$sk_get_local_addresses

  PROCEDURE [XDCL, #GATE] nap$sk_get_local_addresses
    (VAR local_addresses: nat$sk_local_addresses;
     VAR count: integer;
     VAR status: ost$status);

    status.normal := TRUE;
    IF nav$sk_socket_layer_active THEN
      nlp$tm_get_local_addresses (local_addresses, count);
    ELSE
      osp$set_status_condition (nae$sk_tcpip_host_not_defined, status);
    IFEND;

  PROCEND nap$sk_get_local_addresses;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$sk_get_socket', EJECT ??
*copy nah$sk_get_socket

  PROCEDURE [XDCL, #GATE] nap$sk_get_socket
    (    application: ^nat$application_name;
         socket_type: nat$sk_socket_type;
     VAR socket_id: nat$sk_socket_identifier;
     VAR status: ost$status);

    VAR
      application_name: nat$application_name,
      application_status: nat$application_status,
      capability: ost$name,
      current_task_id: ost$global_task_id,
      global_socket: ^nlt$udp_global_socket,
      job_socket: nat$sk_job_socket,
      max_sockets: nat$number_of_sockets,
      old_job_socket: ^nat$sk_job_socket,
      protocol: nat$protocol,
      ring: ost$ring,
      system_privilege: boolean;

    status.normal := TRUE;
    IF nav$sk_socket_layer_active THEN
      IF (socket_type = nac$sk_udp_socket) OR (socket_type = nac$sk_tcp_socket) THEN
        IF application = NIL THEN
          IF socket_type = nac$sk_udp_socket THEN
            application_name := nac$sk_unnamed_udp_application;
          ELSEIF socket_type = nac$sk_tcp_socket THEN
            application_name := nac$sk_unnamed_tcp_application;
          IFEND;
        ELSE { Known application
          application_name := application^;
        IFEND;
      ELSE { unknown socket type
        osp$set_status_condition (nae$sk_unknown_socket_type, status);
      IFEND;

{ Verify that
{    . application name is defined
{    . application is active
{    . required caller ring and capability
{    . max connection not exceeded

      IF status.normal THEN
        osp$push_inhibit_job_recovery;
        nap$get_tcpip_attributes (application_name, application_status, max_sockets, capability, ring,
              system_privilege, protocol, status);
        IF status.normal THEN
          nap$validate_user (capability, ring, system_privilege, status);
          IF status.normal THEN
            IF ((socket_type = nac$sk_udp_socket) AND (protocol = nac$datagram_socket)) OR
                  ((socket_type = nac$sk_tcp_socket) AND (protocol = nac$stream_socket)) THEN
              IF application_status = nac$application_active THEN
                nlp$sk_get_socket_id (socket_id, status);
                IF status.normal THEN
                  pmp$get_executing_task_gtid (current_task_id);
                  nlp$sk_lock_job_socket (socket_id, old_job_socket);
                  IF old_job_socket = NIL THEN

{ Setup the job socket entry. Save the application, application type, capability
{ and ring in the job socket entry.

                    job_socket.identifier := socket_id;
                    job_socket.time_stamp := #FREE_RUNNING_CLOCK (0);
                    job_socket.status := nac$sk_socket_unbound;
                    job_socket.socket_type := socket_type;
                    job_socket.application := application_name;
                    job_socket.capability := capability;
                    job_socket.ring := ring;
                    job_socket.system_privilege := system_privilege;
                    job_socket.port := 0;

{ Initialize the socket attributes to the default values.

                    job_socket.bound_address := nac$sk_all_ip_addresses;
                    job_socket.owner := current_task_id;
                    job_socket.interface_mode := nac$sk_blocking_mode;
                    job_socket.interface_timeout := nac$sk_default_if_timeout;
                    IF socket_type = nac$sk_udp_socket THEN
                      job_socket.traffic_pattern := nac$sk_udp_random_traffic;
                      job_socket.broadcast_enabled := FALSE;
                      nlp$udp_create_global_socket (global_socket);
                      IF global_socket <> NIL THEN
                        job_socket.global_socket_id := global_socket^.identifier;
                        job_socket.checksum := TRUE;
                        job_socket.local_ip_address_enabled := FALSE;
                        job_socket.user_cache_enabled := TRUE;

{ Initialize global socket. Note job_socket is a local variable.

                        initialize_global_socket (job_socket, global_socket);

                        nlp$udp_free_exclusive_access (global_socket);
                        nlp$tcpip_increment_appl_access (application_name, {assigned} TRUE,
                              job_socket.global_socket_id, nac$null_connection_id, status);
                        IF status.normal THEN
                          nlp$sk_add_job_socket (socket_id, job_socket);
                        ELSE
                          IF status.condition = nae$maximum_sockets_exceeded THEN
                            osp$set_status_condition (nae$sk_max_sockets_limit, status);
                          IFEND;
                          nlp$udp_delete_global_socket (job_socket.global_socket_id);
                          nlp$sk_free_socket_id (socket_id);
                        IFEND;
                      ELSE { global_socket = NIL
                        nlp$sk_free_socket_id (socket_id);
                        osp$set_status_abnormal (nac$status_id, nae$sk_insufficient_resources,
                              'NAP$SK_GET_SOCKET', status);
                      IFEND;
                    ELSEIF socket_type = nac$sk_tcp_socket THEN
                      job_socket.traffic_pattern := nac$sk_tcp_random_traffic;
                      job_socket.tcp_socket_type := nlc$tcp_null_socket;
                      job_socket.connection_id := nac$null_connection_id;
                      job_socket.local_ip_address := 0;
                      job_socket.reuse_address := FALSE;
                      job_socket.graceful_close := TRUE;
                      job_socket.selection_criteria.ip_address := nac$sk_all_ip_addresses;
                      job_socket.selection_criteria.port := 0;
                      nlp$sk_add_job_socket (socket_id, job_socket);
                    IFEND;
                  ELSE { Assigned socket already in use
                    nap$namve_system_error ({Recoverable_error=} TRUE, 'Assigned_socket id already in use.',
                          NIL);
                    osp$set_status_abnormal (nac$status_id, nae$sk_internal_error, 'GET SOCKET', status);
                  IFEND;
                  nlp$sk_unlock_job_socket (socket_id);
                IFEND;
              ELSE { application_status <> nac$application_active
                osp$set_status_abnormal (nac$status_id, nae$application_inactive, application_name, status);
              IFEND;
            ELSE
              osp$set_status_condition (nae$sk_protocol_mismatch, status);
            IFEND;
          ELSE { Invalid user
            osp$set_status_abnormal (nac$status_id, nae$sk_invalid_user, 'NAP$SK_GET_SOCKET', status);
          IFEND;
        IFEND;
        osp$pop_inhibit_job_recovery;
      IFEND;
    ELSE { Socket layer not active
      osp$set_status_condition (nae$sk_tcpip_host_not_defined, status);
    IFEND;

  PROCEND nap$sk_get_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$sk_get_socket_attributes', EJECT ??
*copy nah$sk_get_socket_attributes

  PROCEDURE [XDCL, #GATE] nap$sk_get_socket_attributes
    (    socket_id: nat$sk_socket_identifier;
     VAR socket_attributes: nat$sk_socket_attributes;
     VAR status: ost$status);

?? NEWTITLE := 'get_optimum_transfer_size', EJECT ??

    PROCEDURE get_optimum_transfer_size
      (    connection_id: nat$connection_id;
       VAR optimum_transfer_unit_size: nat$data_length);

?? PUSH (LISTEXT := ON) ??
*copyc nlt$cc_connection
?? POP ??

      VAR
        cc_connection: ^nlt$cc_connection,
        cl_connection: ^nlt$cl_connection,
        connection_exists: boolean,
        layer_active: boolean;

      nlp$cl_get_exclusive_via_cid (connection_id, connection_exists, cl_connection);
      IF cl_connection <> NIL THEN
        nlp$cl_get_layer_connection (nlc$channel_connection_layer, cl_connection, layer_active,
              cc_connection);
        IF cc_connection <> NIL THEN
          optimum_transfer_unit_size := cc_connection^.device_specific_attributes.maximum_data_length -
                nlc$lower_layer_overhead;
        ELSE
          optimum_transfer_unit_size := 0;
        IFEND;
        nlp$cl_release_exclusive_access (cl_connection);
      ELSE
        optimum_transfer_unit_size := 0;
      IFEND;

    PROCEND get_optimum_transfer_size;

?? OLDTITLE ??
?? NEWTITLE := 'terminate_get_attributes', EJECT ??

    PROCEDURE terminate_get_attributes
      (    condition: pmt$condition;
           ignore_condition_descriptor: ^pmt$condition_information;
           sa: ^ost$stack_frame_save_area;
       VAR condition_status: ost$status);

      nap$condition_handler_trace (condition, sa);
      CASE condition.selector OF
      = pmc$system_conditions, mmc$segment_access_condition =
        nlp$sk_clear_job_socket_lock (socket_id);
        osp$pop_inhibit_job_recovery;
        osp$set_status_from_condition (nac$status_id, condition, sa, status, condition_status);
        condition_status.normal := TRUE;
        EXIT nap$sk_get_socket_attributes; {----->
      = pmc$user_defined_condition =
        IF condition.user_condition_name = 'OSC$JOB_RECOVERY' THEN
          pmp$continue_to_cause (pmc$execute_standard_procedure, condition_status);
        IFEND;
        condition_status.normal := TRUE;
      ELSE
        condition_status.normal := TRUE;
      CASEND;

    PROCEND terminate_get_attributes;
?? OLDTITLE ??
?? EJECT ??

    VAR
      attribute_p: ^nat$sk_socket_attribute,
      caller_id: ost$caller_identifier,
      i: integer,
      job_socket: ^nat$sk_job_socket,
      optimum_transfer_size: nat$data_length;

    status.normal := TRUE;
    osp$push_inhibit_job_recovery;
    osp$establish_condition_handler (^terminate_get_attributes, FALSE);
    IF socket_id > 0 THEN
      nlp$sk_lock_job_socket (socket_id, job_socket);
      IF job_socket <> NIL THEN
        #CALLER_ID (caller_id);
        IF caller_id.ring <= job_socket^.ring THEN

        /return_socket_attributes/
          FOR i := 1 TO UPPERBOUND (socket_attributes) DO
            attribute_p := ^socket_attributes [i];
            CASE attribute_p^.attribute_kind OF
            = nac$sk_interface_mode_attr =
              attribute_p^.interface_mode := job_socket^.interface_mode;

            = nac$sk_interface_timeout_attr =
              attribute_p^.interface_timeout := job_socket^.interface_timeout;

            = nac$sk_checksum_attr =
              IF job_socket^.socket_type = nac$sk_udp_socket THEN
                attribute_p^.checksum := job_socket^.checksum;
              ELSEIF job_socket^.socket_type = nac$sk_tcp_socket THEN
                osp$set_status_abnormal (nac$status_id, nae$sk_invalid_attribute, 'TCP', status);
                osp$append_status_integer (osc$status_parameter_delimiter, i, 10, TRUE, status);
                EXIT /return_socket_attributes/; {----->
              IFEND;

            = nac$sk_traffic_pattern_attr =
              attribute_p^.traffic_pattern := job_socket^.traffic_pattern;

            = nac$sk_graceful_close_attr =
              IF job_socket^.socket_type = nac$sk_tcp_socket THEN
                attribute_p^.graceful_close := job_socket^.graceful_close;
              ELSEIF job_socket^.socket_type = nac$sk_udp_socket THEN
                osp$set_status_abnormal (nac$status_id, nae$sk_invalid_attribute, 'UDP', status);
                osp$append_status_integer (osc$status_parameter_delimiter, i, 10, TRUE, status);
                EXIT /return_socket_attributes/; {----->
              IFEND;

            = nac$sk_selection_criteria_attr =
              IF job_socket^.socket_type = nac$sk_tcp_socket THEN
                attribute_p^.port := job_socket^.selection_criteria.port;
                attribute_p^.ip_address := job_socket^.selection_criteria.ip_address;
              ELSEIF job_socket^.socket_type = nac$sk_udp_socket THEN
                osp$set_status_abnormal (nac$status_id, nae$sk_invalid_attribute, 'UDP', status);
                osp$append_status_integer (osc$status_parameter_delimiter, i, 10, TRUE, status);
                EXIT /return_socket_attributes/; {----->
              IFEND;

            = nac$sk_local_addr_enabled_attr =
              IF job_socket^.socket_type = nac$sk_udp_socket THEN
                attribute_p^.local_ip_address_enabled := job_socket^.local_ip_address_enabled;
              ELSEIF job_socket^.socket_type = nac$sk_tcp_socket THEN
                osp$set_status_abnormal (nac$status_id, nae$sk_invalid_attribute, 'TCP', status);
                osp$append_status_integer (osc$status_parameter_delimiter, i, 10, TRUE, status);
                EXIT /return_socket_attributes/; {----->
              IFEND;

            = nac$sk_broadcast_enabled_attr =
              IF job_socket^.socket_type = nac$sk_udp_socket THEN
                attribute_p^.broadcast_enabled := job_socket^.broadcast_enabled;
              ELSEIF job_socket^.socket_type = nac$sk_tcp_socket THEN
                osp$set_status_abnormal (nac$status_id, nae$sk_invalid_attribute, 'TCP', status);
                osp$append_status_integer (osc$status_parameter_delimiter, i, 10, TRUE, status);
                EXIT /return_socket_attributes/; {----->
              IFEND;

            = nac$sk_optimum_xfer_size_attr =
              IF job_socket^.socket_type = nac$sk_tcp_socket THEN
                IF job_socket^.connection_id <> nac$null_connection_id THEN
                  get_optimum_transfer_size (job_socket^.connection_id, optimum_transfer_size);
                  attribute_p^.optimum_transfer_size := optimum_transfer_size;
                ELSE
                  attribute_p^.optimum_transfer_size := 0;
                IFEND;
              ELSEIF job_socket^.socket_type = nac$sk_udp_socket THEN
                osp$set_status_abnormal (nac$status_id, nae$sk_invalid_attribute, 'UDP', status);
                osp$append_status_integer (osc$status_parameter_delimiter, i, 10, TRUE, status);
                EXIT /return_socket_attributes/; {----->
              IFEND;

            = nac$sk_user_cache_enabled_attr =
              IF job_socket^.socket_type = nac$sk_udp_socket THEN
                attribute_p^.user_cache_enabled := job_socket^.user_cache_enabled;
              ELSEIF job_socket^.socket_type = nac$sk_tcp_socket THEN
                osp$set_status_abnormal (nac$status_id, nae$sk_invalid_attribute, 'TCP', status);
                osp$append_status_integer (osc$status_parameter_delimiter, i, 10, TRUE, status);
                EXIT /return_socket_attributes/; {----->
              IFEND;

            = nac$sk_reuse_address_attr =
              IF job_socket^.socket_type = nac$sk_tcp_socket THEN
                attribute_p^.reuse_address := job_socket^.reuse_address;
              ELSEIF job_socket^.socket_type = nac$sk_udp_socket THEN
                osp$set_status_abnormal (nac$status_id, nae$sk_invalid_attribute, 'UDP', status);
                osp$append_status_integer (osc$status_parameter_delimiter, i, 10, TRUE, status);
                EXIT /return_socket_attributes/; {----->
              IFEND;

            = nac$sk_local_address_attr =
              IF job_socket^.status <> nac$sk_socket_unbound THEN
                attribute_p^.local_port := job_socket^.port;
                IF job_socket^.bound_address <> nac$sk_all_ip_addresses THEN
                  attribute_p^.address_count := 1;
                  attribute_p^.local_addresses^ [1] := job_socket^.bound_address;
                ELSE { socket bound to all known ip addresses
                  IF job_socket^.socket_type = nac$sk_udp_socket THEN
                    nlp$udp_get_bound_addresses (job_socket^.global_socket_id, attribute_p^.local_addresses^,
                          attribute_p^.address_count, status);
                    IF NOT status.normal THEN
                      IF status.condition = nae$sk_socket_terminated THEN
                        nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                        osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                              status);
                      IFEND;
                      EXIT /return_socket_attributes/; {----->
                    IFEND;
                  ELSEIF job_socket^.socket_type = nac$sk_tcp_socket THEN
                    IF job_socket^.tcp_socket_type = nlc$tcp_listen_socket THEN
                      nlp$sk_tcp_get_listen_addresses (job_socket^.application, job_socket^.port,
                            job_socket^.bound_address, attribute_p^.local_addresses^,
                            attribute_p^.address_count, status);
                      IF NOT status.normal THEN
                        IF status.condition = nae$sk_socket_terminated THEN
                          nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                          osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                                status);
                        IFEND;
                        EXIT /return_socket_attributes/; {----->
                      IFEND;
                    ELSE
                      IF (job_socket^.tcp_socket_type = nlc$tcp_connect_socket) OR
                            (job_socket^.tcp_socket_type = nlc$tcp_accept_socket) THEN

{ A connect or an accept socket may have been bound to all known IP addresses but
{ it is connected over one IP address only.

                        attribute_p^.address_count := 1;
                        attribute_p^.local_addresses^ [1] := job_socket^.local_ip_address;
                      ELSE { connection not estableshed
                        attribute_p^.address_count := 0;

{ This is the case of a TCP socket that has been bound to all IP addresses but
{ the listen has not been done or the connection has not been established i.e.,
{ the connect socket request has not been issued.

                      IFEND;
                    IFEND;
                  IFEND;
                IFEND;
              ELSE { Socket is unbound.
                attribute_p^.address_count := 0;
                attribute_p^.local_port := 0;
              IFEND;
            ELSE {Invalid socket attribute
              osp$set_status_condition (nae$sk_unknown_attribute, status);
              osp$append_status_integer (osc$status_parameter_delimiter,
                    $INTEGER (attribute_p^.attribute_kind), 10, TRUE, status);
              EXIT /return_socket_attributes/; {----->
            CASEND;
          FOREND /return_socket_attributes/;
        ELSE { Invalid user
          osp$set_status_abnormal (nac$status_id, nae$sk_invalid_user, 'NAP$SK_GET_SOCKET_ATTRIBUTES',
                status);
        IFEND;
      ELSE { Unknown socket
        osp$set_status_condition (nae$sk_unknown_socket, status);
        osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
      IFEND;
      nlp$sk_unlock_job_socket (socket_id);
    ELSE { socket_id = 0
      osp$set_status_condition (nae$sk_unknown_socket, status);
      osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
    IFEND;
    osp$pop_inhibit_job_recovery;

  PROCEND nap$sk_get_socket_attributes;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$sk_get_socket_status', EJECT ??

*copy nah$sk_get_socket_status

  PROCEDURE [XDCL, #GATE] nap$sk_get_socket_status
    (    socket_id: nat$sk_socket_identifier;
     VAR socket_status: nat$sk_socket_status;
     VAR status: ost$status);

    VAR
      caller_id: ost$caller_identifier,
      job_socket: ^nat$sk_job_socket;

    status.normal := TRUE;
    osp$push_inhibit_job_recovery;
    IF socket_id > 0 THEN
      nlp$sk_lock_job_socket (socket_id, job_socket);
      IF job_socket <> NIL THEN
        #CALLER_ID (caller_id);
        IF caller_id.ring <= job_socket^.ring THEN
          IF job_socket^.status = nac$sk_socket_open THEN
            IF job_socket^.socket_type = nac$sk_udp_socket THEN
              socket_status.connection_pending := FALSE;
              nlp$udp_get_socket_status (job_socket^.global_socket_id, socket_status.clear_to_send,
                    socket_status.data_pending_receive, status);
              IF (NOT status.normal) AND (status.condition = nae$sk_socket_terminated) THEN
                nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
              IFEND;
            ELSEIF job_socket^.socket_type = nac$sk_tcp_socket THEN
              IF job_socket^.tcp_socket_type = nlc$tcp_listen_socket THEN
                nlp$sk_tcp_check_accept_socket (job_socket^.application, job_socket^.port,
                      job_socket^.bound_address, FALSE {wait} , socket_status.connection_pending);
                socket_status.clear_to_send := FALSE;
                socket_status.data_pending_receive := 0;
              ELSEIF job_socket^.tcp_socket_type <> nlc$tcp_null_socket THEN
                socket_status.connection_pending := FALSE;
                nlp$sk_tcp_get_socket_status (job_socket^.connection_id, socket_status.clear_to_send,
                      socket_status.data_pending_receive, status);
                IF NOT status.normal THEN
                  IF status.condition = nae$sk_socket_disconnected THEN
                    nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_disconnected);
                    osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                  ELSEIF status.condition = nae$sk_socket_closed_via_peer THEN
                    nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_closed_via_peer);
                    osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                  ELSEIF status.condition = nae$sk_socket_terminated THEN
                    nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                    osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                  IFEND;
                IFEND;
              ELSE
                socket_status.clear_to_send := FALSE;
                socket_status.data_pending_receive := 0;
                socket_status.connection_pending := FALSE;
              IFEND;
            IFEND;
          ELSEIF job_socket^.status = nac$sk_socket_unbound THEN
            osp$set_status_condition (nae$sk_unbound_socket, status);
            osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
          ELSEIF job_socket^.status = nac$sk_socket_disconnected THEN
            osp$set_status_condition (nae$sk_socket_disconnected, status);
            osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
          ELSEIF job_socket^.status = nac$sk_socket_closed_via_peer THEN
            osp$set_status_condition (nae$sk_socket_closed_via_peer, status);
            osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
          ELSEIF job_socket^.status = nac$sk_socket_terminated THEN
            osp$set_status_condition (nae$sk_socket_terminated, status);
            osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
          ELSEIF job_socket^.status = nac$sk_job_recovery THEN
            osp$set_status_condition (nae$sk_job_recovery, status);
            osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
          IFEND;
        ELSE { Invalid user
          osp$set_status_abnormal (nac$status_id, nae$sk_invalid_user, 'NAP$SK_GET_SOCKET_STATUS', status);
        IFEND;
      ELSE { job_socket = NIL
        osp$set_status_condition (nae$sk_unknown_socket, status);
        osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
      IFEND;

      nlp$sk_unlock_job_socket (socket_id);
    ELSE { socket_id = 0
      osp$set_status_condition (nae$sk_unknown_socket, status);
      osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
    IFEND;
    osp$pop_inhibit_job_recovery;

  PROCEND nap$sk_get_socket_status;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$sk_listen_socket', EJECT ??
*copy nah$sk_listen_socket

  PROCEDURE [XDCL, #GATE] nap$sk_listen_socket
    (    socket_id: nat$sk_socket_identifier;
         queue_limit: nat$sk_listen_queue_limit;
     VAR status: ost$status);

    VAR
      caller_id: ost$caller_identifier,
      job_socket: ^nat$sk_job_socket,
      port: nat$sk_port_number;

    status.normal := TRUE;
    osp$push_inhibit_job_recovery;
    IF socket_id > 0 THEN
      nlp$sk_lock_job_socket (socket_id, job_socket);
      IF (job_socket <> NIL) THEN
        #CALLER_ID (caller_id);
        IF caller_id.ring <= job_socket^.ring THEN
          IF job_socket^.socket_type = nac$sk_tcp_socket THEN
            IF job_socket^.status = nac$sk_socket_unbound THEN
              open_tcp_port (job_socket^.bound_address, port, status);
            ELSEIF job_socket^.status = nac$sk_socket_open THEN
              port := job_socket^.port;
              IF job_socket^.tcp_socket_type = nlc$tcp_listen_socket THEN
                osp$set_status_condition (nae$sk_listen_already_active, status);
                osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
              ELSEIF job_socket^.tcp_socket_type <> nlc$tcp_null_socket THEN
                osp$set_status_condition (nae$sk_socket_already_connected, status);
                osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
              IFEND;
            ELSEIF job_socket^.status = nac$sk_socket_terminated THEN
              osp$set_status_condition (nae$sk_socket_terminated, status);
              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
            ELSEIF job_socket^.status = nac$sk_job_recovery THEN
              osp$set_status_condition (nae$sk_job_recovery, status);
              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
            IFEND;

            IF status.normal THEN
              mark_listen_port (port, job_socket^.bound_address, status);
              IF status.normal THEN
                nlp$sk_tcp_activate_listen (socket_id, job_socket^.application, port,
                      job_socket^.bound_address, queue_limit, job_socket^.selection_criteria, status);
                IF status.normal THEN
                  IF job_socket^.status = nac$sk_socket_unbound THEN
                    nlp$sk_update_job_socket (socket_id, port, nac$sk_all_ip_addresses, nac$sk_socket_open);
                  IFEND;
                  nlp$sk_update_listen_socket (socket_id, port);
                ELSE
                  IF job_socket^.status = nac$sk_socket_unbound THEN
                    close_tcp_port (nac$sk_all_ip_addresses, port, TRUE);
                  IFEND;
                  IF status.condition = nae$sk_socket_terminated THEN
                    nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                    osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                  IFEND;
                IFEND;
              ELSEIF job_socket^.status = nac$sk_socket_unbound THEN
                close_tcp_port (nac$sk_all_ip_addresses, port, FALSE);
              IFEND;
            IFEND;
          ELSE { Incorrect socket type
            osp$set_status_abnormal (nac$status_id, nae$sk_incorrect_socket_type, 'LISTEN SOCKET', status);
          IFEND;
        ELSE { Invalid user
          osp$set_status_abnormal (nac$status_id, nae$sk_invalid_user, 'NAP$SK_LISTEN SOCKET', status);
        IFEND;
      ELSE { Unknown socket
        osp$set_status_condition (nae$sk_unknown_socket, status);
        osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
      IFEND;

      nlp$sk_unlock_job_socket (socket_id);
    ELSE { socket_id = 0
      osp$set_status_condition (nae$sk_unknown_socket, status);
      osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
    IFEND;
    osp$pop_inhibit_job_recovery;

  PROCEND nap$sk_listen_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$sk_offer_socket', EJECT ??
*copy nah$sk_offer_socket

  PROCEDURE [XDCL, #GATE] nap$sk_offer_socket
    (    socket_id: nat$sk_socket_identifier;
         destination_job: jmt$system_supplied_name;
         wait_time: nat$wait_time;
     VAR status: ost$status);

?? NEWTITLE := 'terminate_offer_socket', EJECT ??

    PROCEDURE terminate_offer_socket
      (    condition: pmt$condition;
           condition_descriptor: ^pmt$condition_information;
           save_area: ^ost$stack_frame_save_area;
       VAR handler_status: ost$status);

      nap$condition_handler_trace (condition, save_area);
      IF job_socket <> NIL THEN
        IF job_socket^.socket_type = nac$sk_udp_socket THEN
          nlp$udp_cancel_socket_offer (job_socket^.global_socket_id, status);
        ELSEIF job_socket^.socket_type = nac$sk_tcp_socket THEN
          nlp$sk_tcp_cancel_socket_offer (job_socket^.connection_id, status);
        IFEND;
        nlp$sk_clear_job_socket_lock (socket_id);
      IFEND;
      osp$pop_inhibit_job_recovery;

    PROCEND terminate_offer_socket;
?? OLDTITLE, EJECT ??

    VAR
      caller_id: ost$caller_identifier,
      connection_id: nat$connection_id,
      current_task_id: ost$global_task_id,
      global_socket_id: nlt$udp_global_socket_id,
      job_socket: ^nat$sk_job_socket,
      offer_accepted: boolean,
      tcp_socket_type: nlt$tcp_socket_type;

    status.normal := TRUE;
    pmp$get_executing_task_gtid (current_task_id);
    job_socket := NIL;
    #SPOIL (job_socket);
    osp$push_inhibit_job_recovery;
    IF socket_id > 0 THEN
      osp$establish_block_exit_hndlr (^terminate_offer_socket);
      nlp$sk_lock_job_socket (socket_id, job_socket);
      #SPOIL (job_socket);
      IF (job_socket <> NIL) THEN
        #CALLER_ID (caller_id);
        IF caller_id.ring <= job_socket^.ring THEN
          IF job_socket^.owner = current_task_id THEN
            IF job_socket^.status = nac$sk_socket_open THEN
              IF job_socket^.socket_type = nac$sk_udp_socket THEN
                connection_id := nac$null_connection_id;
                global_socket_id := job_socket^.global_socket_id;
                nlp$udp_offer_socket (job_socket^.global_socket_id, status);
                IF (NOT status.normal) AND (status.condition = nae$sk_socket_terminated) THEN
                  nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                  osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                IFEND;
              ELSEIF job_socket^.socket_type = nac$sk_tcp_socket THEN
                IF (job_socket^.tcp_socket_type = nlc$tcp_connect_socket) OR
                      (job_socket^.tcp_socket_type = nlc$tcp_accept_socket) THEN
                  connection_id := job_socket^.connection_id;
                  global_socket_id := nlc$udp_null_global_socket_id;
                  tcp_socket_type := job_socket^.tcp_socket_type;
                  nlp$sk_tcp_offer_socket (connection_id, status);
                  IF NOT status.normal THEN
                    IF status.condition = nae$sk_socket_disconnected THEN
                      nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_disconnected);
                    ELSEIF status.condition = nae$sk_socket_closed_via_peer THEN
                      nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_closed_via_peer);
                    ELSEIF status.condition = nae$sk_socket_terminated THEN
                      nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                      osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                    IFEND;
                  IFEND;
                ELSEIF job_socket^.tcp_socket_type = nlc$tcp_listen_socket THEN
                  osp$set_status_condition (nae$sk_listen_already_active, status);
                  osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                ELSEIF job_socket^.tcp_socket_type = nlc$tcp_null_socket THEN
                  osp$set_status_condition (nae$sk_socket_not_connected, status);
                  osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                IFEND;
              IFEND;
              IF status.normal THEN
                nlp$sk_offer_socket (socket_id, destination_job, job_socket^.socket_type, global_socket_id,
                      connection_id, tcp_socket_type, job_socket^.port, job_socket^.bound_address,
                      job_socket^.traffic_pattern, job_socket^.application, job_socket^.ring,
                      job_socket^.capability, wait_time, offer_accepted);
                IF offer_accepted THEN

{ The global socket or the TCP connection have been switched to the destination job.

                  nlp$sk_delete_job_socket (socket_id, job_socket);
                  nlp$sk_free_socket_id (socket_id);
                ELSE { NOT offer_accepted
                  IF job_socket^.socket_type = nac$sk_udp_socket THEN
                    nlp$udp_cancel_socket_offer (job_socket^.global_socket_id, status);
                    IF (NOT status.normal) AND (status.condition = nae$sk_socket_terminated) THEN
                      nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                      osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                    IFEND;
                  ELSEIF job_socket^.socket_type = nac$sk_tcp_socket THEN
                    nlp$sk_tcp_cancel_socket_offer (job_socket^.connection_id, status);
                    IF NOT status.normal THEN
                      IF status.condition = nae$sk_socket_disconnected THEN
                        nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_disconnected);
                      ELSEIF status.condition = nae$sk_socket_closed_via_peer THEN
                        nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_closed_via_peer);
                      ELSEIF status.condition = nae$sk_socket_terminated THEN
                        nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                        osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                              status);
                      IFEND;
                    IFEND;
                  IFEND;
                  IF status.normal THEN
                    osp$set_status_abnormal (nac$status_id, nae$sk_offer_not_accepted, destination_job,
                          status);
                  IFEND;
                IFEND;
              IFEND;
            ELSEIF job_socket^.status = nac$sk_socket_unbound THEN
              osp$set_status_condition (nae$sk_unbound_socket, status);
              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
            ELSEIF job_socket^.status = nac$sk_socket_disconnected THEN
              osp$set_status_condition (nae$sk_socket_disconnected, status);
              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
            ELSEIF job_socket^.status = nac$sk_socket_closed_via_peer THEN
              osp$set_status_condition (nae$sk_socket_closed_via_peer, status);
              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
            ELSEIF job_socket^.status = nac$sk_socket_terminated THEN
              osp$set_status_condition (nae$sk_socket_terminated, status);
              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
            ELSEIF job_socket^.status = nac$sk_job_recovery THEN
              osp$set_status_condition (nae$sk_job_recovery, status);
              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
            IFEND;
          ELSE { job_socket^.owner <> current_task_id
            osp$set_status_abnormal (nac$status_id, nae$sk_caller_not_the_owner, 'OFFER SOCKET', status);
          IFEND;
        ELSE { invalid user
          osp$set_status_abnormal (nac$status_id, nae$sk_invalid_user, 'NAP$SK_OFFER_SOCKET', status);
          osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
        IFEND;
      ELSE { unknown socket
        osp$set_status_condition (nae$sk_unknown_socket, status);
        osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
      IFEND;
      nlp$sk_unlock_job_socket (socket_id);
      osp$disestablish_cond_handler;
    ELSE { socket_id = 0
      osp$set_status_condition (nae$sk_unknown_socket, status);
      osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
    IFEND;
    osp$pop_inhibit_job_recovery;

  PROCEND nap$sk_offer_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] nap$sk_process_job_termination', EJECT ??
*copy nah$sk_process_job_termination

  PROCEDURE [XDCL] nap$sk_process_job_termination;

    VAR
      ignore_status: ost$status,
      job_socket: ^nat$sk_job_socket,
      socket_id: nat$sk_socket_identifier;

{ The nav$sk_job_socket_assignment is not locked as no other task must be active
{ in this job when this procedure is invoked.

    osp$push_inhibit_job_recovery;
    IF nav$sk_job_socket_assignment.assigned_sockets <> NIL THEN
      FOR socket_id := 1 TO UPPERBOUND (nav$sk_job_socket_assignment.assigned_sockets^) DO
        IF nav$sk_job_socket_assignment.assigned_sockets^ [socket_id] THEN
          job_socket := nav$sk_job_socket_list^ [socket_id].job_socket;
          IF job_socket <> NIL THEN
            close_socket (job_socket, ignore_status);
          IFEND;
        IFEND;
      FOREND;
    IFEND;
    osp$pop_inhibit_job_recovery;

  PROCEND nap$sk_process_job_termination;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$sk_read_socket', EJECT ??
*copy nah$sk_read_socket

  PROCEDURE [XDCL, #GATE] nap$sk_read_socket
    (    socket_id: nat$sk_socket_identifier;
     VAR urgent_flag: boolean;
         data { input, output} : nat$data_fragments;
     VAR data_transferred: integer;
     VAR status: ost$status);

?? NEWTITLE := 'terminate_read_socket', EJECT ??

    PROCEDURE terminate_read_socket
      (    condition: pmt$condition;
           ignore_condition_descriptor: ^pmt$condition_information;
           sa: ^ost$stack_frame_save_area;
       VAR condition_status: ost$status);

      nap$condition_handler_trace (condition, sa);
      CASE condition.selector OF
      = pmc$system_conditions, mmc$segment_access_condition =
        IF cl_connection <> NIL THEN
          nlp$cl_clear_exclusive_access (cl_connection);
        IFEND;
        IF job_socket <> NIL THEN
          nlp$sk_clear_job_socket_lock (socket_id);
        IFEND;
        osp$pop_inhibit_job_recovery;
        osp$set_status_from_condition (nac$status_id, condition, sa, status, condition_status);
        condition_status.normal := TRUE;
        EXIT nap$sk_read_socket; {----->
      = pmc$user_defined_condition =
        IF condition.user_condition_name = 'OSC$JOB_RECOVERY' THEN
          pmp$continue_to_cause (pmc$execute_standard_procedure, condition_status);
        IFEND;
        condition_status.normal := TRUE;
      = pmc$block_exit_processing =
        nlp$cl_get_exclusive_via_cid (connection_id, connection_exists, cl_connection);
        IF cl_connection <> NIL THEN
          nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active, tcp_connection);
          IF layer_active THEN

{ Dequeue the receiver task from the receive queue.

            previous_receiver_task := ^tcp_connection^.receive_queue;
            receiver_task := tcp_connection^.receive_queue;
            WHILE (receiver_task <> NIL) AND (receiver_task^.task_id <> current_task_id) DO
              previous_receiver_task := ^receiver_task^.next_entry;
              receiver_task := receiver_task^.next_entry;
            WHILEND;
            IF receiver_task <> NIL THEN
              IF receiver_task^.received_data_length^ > 0 THEN
                data_transferred := receiver_task^.received_data_length^;
                urgent_flag := receiver_task^.urgent_flag^;
              ELSE
                osp$set_status_condition (nae$sk_no_data_available, status);
                osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
              IFEND;
              previous_receiver_task^ := receiver_task^.next_entry;
              nlp$sk_tcp_ret_rec_task_entry (tcp_connection, receiver_task);
            IFEND;
          IFEND;
          nlp$cl_release_exclusive_access (cl_connection);
        IFEND;
        osp$pop_inhibit_job_recovery;
        condition_status.normal := TRUE;
      ELSE

{ Note: Interactive condition is being ignored.

        condition_status.normal := TRUE;
      CASEND;

    PROCEND terminate_read_socket;
?? OLDTITLE ??
?? EJECT ??

    VAR
      activity_status: ost$activity_status,
      buffer_capacity: nat$data_length,
      buffers_freed: nat$data_length,
      caller_id: ost$caller_identifier,
      cl_connection: ^nlt$cl_connection,
      connection_exists: boolean,
      connection_id: nat$connection_id,
      current_task_id: ost$global_task_id,
      current_time: ost$free_running_clock,
      data_area: ^nat$data_fragments,
      delivered_data_length: integer,
      end_time: integer,
      ignore_status: ost$status,
      interface_mode: nat$sk_interface_mode,
      interface_timeout: nat$wait_time,
      job_socket: ^nat$sk_job_socket,
      layer_active: boolean,
      previous_receiver_task: ^^nlt$tcp_receiver_task,
      received_data: ^nlt$tcp_received_data,
      received_data_length: integer,
      receiver_active: boolean,
      receiver_task: ^nlt$tcp_receiver_task,
      remaining_capacity: integer,
      remaining_time: integer,
      tcp_connection: ^nlt$tcp_socket_layer,
      urgent: boolean;

    IF socket_id = 0 THEN
      osp$set_status_condition (nae$sk_unknown_socket, status);
      osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
      RETURN; {----->
    IFEND;

    status.normal := TRUE;
    data_transferred := 0;
    received_data_length := 0;
    urgent := FALSE;
    receiver_active := FALSE;
    cl_connection := NIL;
    #SPOIL (cl_connection);
    pmp$get_executing_task_gtid (current_task_id);
    #SPOIL (current_task_id);
    osp$push_inhibit_job_recovery;
    osp$establish_condition_handler (^terminate_read_socket, TRUE);

    nlp$sk_lock_job_socket (socket_id, job_socket);
    #SPOIL (job_socket);
    IF (job_socket <> NIL) THEN
      #CALLER_ID (caller_id);
      IF caller_id.ring <= job_socket^.ring THEN
        IF job_socket^.socket_type = nac$sk_tcp_socket THEN
          IF job_socket^.status = nac$sk_socket_open THEN
            IF job_socket^.connection_id <> nac$null_connection_id THEN
              connection_id := job_socket^.connection_id;
              #SPOIL (connection_id);
              interface_mode := job_socket^.interface_mode;
              interface_timeout := job_socket^.interface_timeout;
              activity_status.complete := FALSE;
              activity_status.status.normal := TRUE;
              #SPOIL (activity_status);

{ Access the channel connection.

              nlp$cl_get_exclusive_via_cid (connection_id, connection_exists, cl_connection);
              #SPOIL (cl_connection);
              IF cl_connection <> NIL THEN
                nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active, tcp_connection);
                IF layer_active THEN
                  IF tcp_connection^.state = nlc$tcp_conn_open THEN
                    PUSH data_area: [1 .. UPPERBOUND (data)];
                    data_area^ := data;
                    nlp$al_get_data_length (data_area^, buffer_capacity);
                    IF buffer_capacity > 0 THEN
                      IF tcp_connection^.receive_queue = NIL THEN
                        IF tcp_connection^.received_data <> NIL THEN

{ There is data queued, ready to be received.

                          received_data := tcp_connection^.received_data;
                          nlp$bm_deliver_message (data_area^, received_data^.message_id, data_transferred,
                                buffers_freed);
                          remaining_capacity := buffer_capacity - data_transferred;
                          tcp_connection^.inventory_report := tcp_connection^.inventory_report -
                                buffers_freed;
                          nlp$cc_report_undelivered_data (cl_connection, tcp_connection^.inventory_report);
                          urgent_flag := received_data^.urgent_flag;
                          IF (remaining_capacity = 0) OR (received_data^.urgent_flag) OR
                                (received_data^.push_flag) THEN
                            activity_status.complete := TRUE;
                            IF received_data^.message_id = nlv$bm_null_message_id THEN
                              tcp_connection^.received_data := received_data^.next_entry;
                              nlp$sk_tcp_ret_rec_data_entry (tcp_connection, received_data);
                            ELSE
                              received_data^.length := received_data^.length - data_transferred;
                              received_data^.buffer_count := received_data^.buffer_count - buffers_freed;
                            IFEND;
                          ELSE { user's buffer can hold more data
                            tcp_connection^.received_data := received_data^.next_entry;
                            nlp$sk_tcp_ret_rec_data_entry (tcp_connection, received_data);
                            received_data_length := data_transferred;
                            IF interface_mode = nac$sk_blocking_mode THEN
                              nlp$sk_tcp_get_rec_task_entry (tcp_connection, receiver_task);
                              receiver_task^.next_entry := NIL;
                              receiver_task^.task_id := current_task_id;
                              receiver_task^.receive_type := nlc$tcp_receive_data;
                              receiver_task^.data_buffer := data_area;
                              receiver_task^.remaining_buffer_capacity := remaining_capacity;
                              receiver_task^.received_data_length := ^received_data_length;
                              receiver_task^.urgent_flag := ^urgent;
                              receiver_task^.activity_status := ^activity_status;

{ Queue the receiver task on the receive queue.

                              tcp_connection^.receive_queue := receiver_task;
                              nlp$cl_activate_receiver (cl_connection);
                              receiver_active := TRUE;
                            ELSE { Non-blocking mode
                              activity_status.complete := TRUE;
                            IFEND;
                          IFEND;
                        ELSE { tcp_connection^.received_data = NIL
                          IF interface_mode = nac$sk_blocking_mode THEN
                            nlp$sk_tcp_get_rec_task_entry (tcp_connection, receiver_task);
                            receiver_task^.next_entry := NIL;
                            receiver_task^.task_id := current_task_id;
                            receiver_task^.receive_type := nlc$tcp_receive_data;
                            receiver_task^.data_buffer := data_area;
                            receiver_task^.remaining_buffer_capacity := buffer_capacity;
                            receiver_task^.received_data_length := ^received_data_length;
                            receiver_task^.urgent_flag := ^urgent;
                            receiver_task^.activity_status := ^activity_status;

{ Queue the receiver task on the receive queue.

                            tcp_connection^.receive_queue := receiver_task;
                            nlp$cl_activate_receiver (cl_connection);
                            receiver_active := TRUE;
                          ELSE { Non-blocking mode
                            osp$set_status_condition (nae$sk_no_data_available, status);
                            osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                                  status);
                          IFEND;
                        IFEND;
                        IF status.normal AND NOT activity_status.complete THEN
                          nlp$cc_receive_data (cl_connection);
                          IF activity_status.complete THEN
                            data_transferred := received_data_length;
                            urgent_flag := urgent;
                          IFEND;
                        IFEND;
                      ELSE { Receive_queue <> NIL
                        IF interface_mode = nac$sk_blocking_mode THEN
                          nlp$sk_tcp_get_rec_task_entry (tcp_connection, receiver_task);
                          receiver_task^.next_entry := NIL;
                          receiver_task^.task_id := current_task_id;
                          receiver_task^.receive_type := nlc$tcp_receive_data;
                          receiver_task^.data_buffer := data_area;
                          receiver_task^.remaining_buffer_capacity := buffer_capacity;
                          receiver_task^.received_data_length := ^received_data_length;
                          receiver_task^.urgent_flag := ^urgent;
                          receiver_task^.activity_status := ^activity_status;

{ Queue the receiver task at the end of the receive queue.

                          previous_receiver_task := ^tcp_connection^.receive_queue;
                          WHILE previous_receiver_task^ <> NIL DO
                            previous_receiver_task := ^previous_receiver_task^^.next_entry;
                          WHILEND;
                          previous_receiver_task^ := receiver_task;
                        ELSE { Non-blocking mode
                          osp$set_status_condition (nae$sk_read_in_progress, status);
                          osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                                status);
                        IFEND;
                      IFEND;
                    ELSE { buffer_capacity = 0
                      osp$set_status_condition (nae$sk_data_area_too_small, status);
                    IFEND;
                  ELSEIF tcp_connection^.state = nlc$tcp_conn_closing THEN
                    PUSH data_area: [1 .. UPPERBOUND (data)];
                    data_area^ := data;
                    nlp$al_get_data_length (data_area^, buffer_capacity);
                    IF buffer_capacity > 0 THEN
                      IF tcp_connection^.receive_queue = NIL THEN
                        IF tcp_connection^.received_data <> NIL THEN

{ There is queued data ready to be received.

                          received_data := tcp_connection^.received_data;
                          nlp$bm_deliver_message (data_area^, received_data^.message_id, data_transferred,
                                buffers_freed);
                          tcp_connection^.inventory_report := tcp_connection^.inventory_report -
                                buffers_freed;
                          urgent_flag := received_data^.urgent_flag;
                          IF received_data^.message_id = nlv$bm_null_message_id THEN
                            tcp_connection^.received_data := received_data^.next_entry;
                            FREE received_data IN nav$network_paged_heap^;

{ If all data has been delivered, update the connection state.

                            IF tcp_connection^.received_data = NIL THEN
                              tcp_connection^.state := nlc$tcp_conn_closed;
                              IF tcp_connection^.disconnect_reason = nlc$tcpaa_ri_user_termination THEN
                                nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_closed_via_peer);
                              ELSE
                                nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_disconnected);
                              IFEND;

{  Do not return an abnormal status yet.

                              IF tcp_connection^.send_queue = NIL THEN
                                nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
                              IFEND;
                            IFEND;
                          ELSE { more data queued
                            received_data^.length := received_data^.length - data_transferred;
                          IFEND;
                        ELSE { received_data = NIL
                          tcp_connection^.state := nlc$tcp_conn_closed;
                          IF tcp_connection^.disconnect_reason = nlc$tcpaa_ri_user_termination THEN
                            osp$set_status_condition (nae$sk_socket_closed_via_peer, status);
                            nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_closed_via_peer);
                          ELSE
                            osp$set_status_condition (nae$sk_socket_disconnected, status);
                            nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_disconnected);
                          IFEND;
                          osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                                status);
                          IF tcp_connection^.send_queue = NIL THEN
                            nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
                          IFEND;
                        IFEND;
                      ELSE { receive_queue <> NIL
                        IF tcp_connection^.disconnect_reason = nlc$tcpaa_ri_user_termination THEN
                          osp$set_status_condition (nae$sk_socket_closed_via_peer, status);
                        ELSE
                          osp$set_status_condition (nae$sk_socket_disconnected, status);
                        IFEND;
                        osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                              status);
                      IFEND;
                    ELSE { buffer_capacity = 0
                      osp$set_status_condition (nae$sk_data_area_too_small, status);
                    IFEND;
                    activity_status.complete := TRUE;
                  ELSEIF tcp_connection^.state = nlc$tcp_conn_closed THEN
                    IF tcp_connection^.disconnect_reason = nlc$tcpaa_ri_user_termination THEN
                      osp$set_status_condition (nae$sk_socket_closed_via_peer, status);
                      nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_closed_via_peer);
                    ELSE
                      osp$set_status_condition (nae$sk_socket_disconnected, status);
                      nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_disconnected);
                    IFEND;
                    osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                    IF (tcp_connection^.send_queue = NIL) AND (tcp_connection^.receive_queue = NIL) THEN
                      nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
                    IFEND;
                  ELSEIF tcp_connection^.state = nlc$tcp_conn_terminated THEN
                    osp$set_status_condition (nae$sk_socket_terminated, status);
                    osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                    IF (tcp_connection^.send_queue = NIL) AND (tcp_connection^.receive_queue = NIL) THEN
                      nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
                    IFEND;
                    nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                  ELSE { All other states
                    nap$namve_system_error ({Recoverable_error=} TRUE, 'Unexpected TCP connection state',
                          NIL);
                    osp$set_status_abnormal (nac$status_id, nae$sk_internal_error, 'READ SOCKET', status);
                  IFEND;
                ELSE { Layer inactive

{ The socket is assumed to have been terminated via application management.

                  nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                  osp$set_status_condition (nae$sk_socket_terminated, status);
                  osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                IFEND;
                nlp$cl_release_exclusive_access (cl_connection);
                cl_connection := NIL;
              ELSE { cl_connection = NIL

{ The socket is assumed to have been terminated via application management.

                nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                osp$set_status_condition (nae$sk_socket_terminated, status);
                osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
              IFEND;

              nlp$sk_unlock_job_socket (socket_id);
              job_socket := NIL;
              #SPOIL (job_socket);

              IF status.normal AND NOT activity_status.complete THEN
                end_time := #FREE_RUNNING_CLOCK (0) + interface_timeout * 1000;
                remaining_time := interface_timeout;
                REPEAT
                  pmp$wait (remaining_time, 0);
                  current_time := #FREE_RUNNING_CLOCK (0);
                  IF current_time < end_time THEN
                    remaining_time := (end_time - current_time) DIV 1000;
                  ELSE
                    remaining_time := 0;
                  IFEND;

                  nlp$cl_get_exclusive_via_cid (connection_id, connection_exists, cl_connection);
                  #SPOIL (cl_connection);
                  IF cl_connection <> NIL THEN
                    nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active,
                          tcp_connection);
                    IF layer_active THEN
                      IF tcp_connection^.state = nlc$tcp_conn_open THEN
                        IF tcp_connection^.receive_queue^.task_id = current_task_id THEN
                          receiver_task := tcp_connection^.receive_queue;

{ The current task is at the head of the queue and hence can receive data.

                          IF NOT receiver_active THEN
                            nlp$cl_activate_receiver (cl_connection);
                            receiver_active := TRUE;
                          IFEND;

                          IF tcp_connection^.received_data <> NIL THEN
                            received_data := tcp_connection^.received_data;
                            nlp$bm_deliver_message (data_area^, received_data^.message_id,
                                  delivered_data_length, buffers_freed);
                            received_data_length := received_data_length + delivered_data_length;
                            urgent := received_data^.urgent_flag;
                            receiver_task^.remaining_buffer_capacity :=
                                  receiver_task^.remaining_buffer_capacity - delivered_data_length;
                            received_data^.length := received_data^.length - delivered_data_length;
                            received_data^.buffer_count := received_data^.buffer_count - buffers_freed;

                            tcp_connection^.inventory_report := tcp_connection^.inventory_report -
                                  buffers_freed;
                            nlp$cc_report_undelivered_data (cl_connection, tcp_connection^.inventory_report);

                            IF (receiver_task^.remaining_buffer_capacity = 0) OR (received_data^.push_flag) OR
                                  (received_data^.urgent_flag) OR (remaining_time = 0) THEN

{ Receive is complete, dequeue receiver task.

                              tcp_connection^.receive_queue := receiver_task^.next_entry;
                              activity_status.complete := TRUE;
                              data_transferred := received_data_length;
                              urgent_flag := urgent;
                              nlp$sk_tcp_ret_rec_task_entry (tcp_connection, receiver_task);
                              nlp$cl_deactivate_receiver (cl_connection);

{ Ready the next task in the receive queue.

                              IF tcp_connection^.receive_queue <> NIL THEN
                                pmp$ready_task (tcp_connection^.receive_queue^.task_id, {ignore} status);
                                status.normal := TRUE;
                              IFEND;
                            IFEND;
                            IF received_data^.message_id = nlv$bm_null_message_id THEN
                              tcp_connection^.received_data := received_data^.next_entry;
                              nlp$sk_tcp_ret_rec_data_entry (tcp_connection, received_data);
                            IFEND;
                          IFEND;
                          IF NOT activity_status.complete THEN
                            nlp$cc_receive_data (cl_connection);
                            IF activity_status.complete THEN
                              IF activity_status.status.normal THEN
                                data_transferred := received_data_length;
                                urgent_flag := urgent;
                              ELSE
                                status := activity_status.status;
                                data_transferred := 0;
                              IFEND;
                            IFEND;
                          IFEND;
                          IF (NOT activity_status.complete) AND (remaining_time = 0) THEN
                            activity_status.complete := TRUE;
                            tcp_connection^.receive_queue := receiver_task^.next_entry;
                            IF received_data_length > 0 THEN
                              data_transferred := received_data_length;
                              urgent_flag := urgent;
                            ELSE
                              osp$set_status_condition (nae$sk_interface_timeout, status);
                              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                                    status);
                            IFEND;
                            nlp$sk_tcp_ret_rec_task_entry (tcp_connection, receiver_task);
                            nlp$cl_deactivate_receiver (cl_connection);
                            IF tcp_connection^.receive_queue <> NIL THEN
                              pmp$ready_task (tcp_connection^.receive_queue^.task_id, ignore_status);
                            IFEND;
                          IFEND;
                        ELSE { task not at the head of the queue
                          IF remaining_time = 0 THEN

{ Dequeue the receiver task.

                            previous_receiver_task := ^tcp_connection^.receive_queue;
                            receiver_task := tcp_connection^.receive_queue;
                            WHILE (receiver_task <> NIL) AND (receiver_task^.task_id <> current_task_id) DO
                              previous_receiver_task := ^receiver_task^.next_entry;
                              receiver_task := receiver_task^.next_entry;
                            WHILEND;
                            IF receiver_task <> NIL THEN
                              previous_receiver_task^ := receiver_task^.next_entry;
                              nlp$sk_tcp_ret_rec_task_entry (tcp_connection, receiver_task);
                              osp$set_status_condition (nae$sk_interface_timeout, status);
                              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                                    status);
                            IFEND;
                          IFEND;
                        IFEND;
                      ELSEIF tcp_connection^.state = nlc$tcp_conn_closing THEN
                        IF tcp_connection^.receive_queue^.task_id = current_task_id THEN

{ Current task is at the head of the queue.

                          receiver_task := tcp_connection^.receive_queue;
                          IF tcp_connection^.received_data <> NIL THEN

{ There is queued data ready to be received.

                            received_data := tcp_connection^.received_data;
                            nlp$bm_deliver_message (data_area^, received_data^.message_id,
                                  delivered_data_length, buffers_freed);
                            tcp_connection^.inventory_report := tcp_connection^.inventory_report -
                                  buffers_freed;
                            urgent_flag := received_data^.urgent_flag;
                            received_data_length := received_data_length + delivered_data_length;
                            IF received_data^.message_id = nlv$bm_null_message_id THEN
                              tcp_connection^.received_data := received_data^.next_entry;
                              FREE received_data IN nav$network_paged_heap^;

{ If all data has been delivered, update the connection state.

                              IF tcp_connection^.received_data = NIL THEN
                                tcp_connection^.state := nlc$tcp_conn_closed;

{ Do not deactivate the layer yet. The next user call will update the job socket status.
{  Do not return an abnormal status yet.

                              IFEND;
                            ELSE { more data queued
                              received_data^.length := received_data^.length - data_transferred;
                              received_data^.buffer_count := received_data^.buffer_count - buffers_freed;
                            IFEND;
                          ELSE { received_data = NIL
                            tcp_connection^.state := nlc$tcp_conn_closed;
                            urgent_flag := urgent;
                            IF received_data_length = 0 THEN
                              IF tcp_connection^.disconnect_reason = nlc$tcpaa_ri_user_termination THEN
                                osp$set_status_condition (nae$sk_socket_closed_via_peer, status);
                              ELSE
                                osp$set_status_condition (nae$sk_socket_disconnected, status);
                              IFEND;
                              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                                    status);
                            IFEND;
                          IFEND;

{ Dequeue and return the receiver task.

                          data_transferred := received_data_length;
                          tcp_connection^.receive_queue := receiver_task^.next_entry;
                          FREE receiver_task IN nav$network_paged_heap^;
                        ELSE { current task is not at the head of the queue.
                          previous_receiver_task := ^tcp_connection^.receive_queue;
                          receiver_task := tcp_connection^.receive_queue;
                          WHILE (receiver_task <> NIL) AND (receiver_task^.task_id <> current_task_id) DO
                            previous_receiver_task := ^receiver_task^.next_entry;
                            receiver_task := receiver_task^.next_entry;
                          WHILEND;
                          IF receiver_task <> NIL THEN
                            previous_receiver_task^ := receiver_task^.next_entry;
                            FREE receiver_task IN nav$network_paged_heap^;
                          IFEND;
                          IF tcp_connection^.disconnect_reason = nlc$tcpaa_ri_user_termination THEN
                            osp$set_status_condition (nae$sk_socket_closed_via_peer, status);
                          ELSE
                            osp$set_status_condition (nae$sk_socket_disconnected, status);
                          IFEND;
                          osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                                status);
                        IFEND;
                        activity_status.complete := TRUE;
                      ELSE { connection closed or terminated

{ Dequeue the receiver task from the receive queue.

                        activity_status.complete := TRUE;
                        previous_receiver_task := ^tcp_connection^.receive_queue;
                        receiver_task := tcp_connection^.receive_queue;
                        WHILE (receiver_task <> NIL) AND (receiver_task^.task_id <> current_task_id) DO
                          previous_receiver_task := ^receiver_task^.next_entry;
                          receiver_task := receiver_task^.next_entry;
                        WHILEND;
                        IF receiver_task <> NIL THEN
                          previous_receiver_task^ := receiver_task^.next_entry;
                          data_transferred := received_data_length;
                          urgent_flag := urgent;
                          FREE receiver_task IN nav$network_paged_heap^;
                        IFEND;

{ Check if the socket has been closed in the meantime.

                        IF (tcp_connection^.state = nlc$tcp_conn_closed) THEN
                          IF tcp_connection^.user_initiated_close THEN
                            IF (tcp_connection^.send_queue = NIL) AND
                                  (tcp_connection^.receive_queue = NIL) THEN
                              nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
                            IFEND;
                            IF data_transferred = 0 THEN
                              osp$set_status_condition (nae$sk_unknown_socket, status);
                              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                                    status);
                            IFEND;
                          ELSEIF data_transferred = 0 THEN
                            IF tcp_connection^.disconnect_reason = nlc$tcpaa_ri_user_termination THEN
                              osp$set_status_condition (nae$sk_socket_closed_via_peer, status);
                            ELSE
                              osp$set_status_condition (nae$sk_socket_disconnected, status);
                            IFEND;
                            osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                                  status);
                          IFEND;
                        ELSEIF tcp_connection^.state = nlc$tcp_conn_terminated THEN
                          IF data_transferred = 0 THEN
                            osp$set_status_condition (nae$sk_socket_terminated, status);
                            osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                                  status);
                            IF (tcp_connection^.send_queue = NIL) AND
                                  (tcp_connection^.receive_queue = NIL) THEN
                              nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
                            IFEND;
                          IFEND;
                        IFEND;
                      IFEND;
                    ELSE { Layer inactive
                      nap$namve_system_error ({Recoverable_error=} TRUE, 'TCP layer connection inactive',
                            NIL);
                      osp$set_status_abnormal (nac$status_id, nae$sk_internal_error, 'READ SOCKET', status);
                    IFEND;
                    nlp$cl_release_exclusive_access (cl_connection);
                    cl_connection := NIL;
                  ELSE { cl_connection = NIL
                    nap$namve_system_error ({Recoverable_error=} TRUE, 'CL connection NIL', NIL);
                    osp$set_status_abnormal (nac$status_id, nae$sk_internal_error, 'READ SOCKET', status);
                  IFEND;
                UNTIL activity_status.complete OR NOT status.normal;
              IFEND;
            ELSE
              nlp$sk_unlock_job_socket (socket_id);
              IF job_socket^.tcp_socket_type = nlc$tcp_listen_socket THEN
                osp$set_status_condition (nae$sk_listen_already_active, status);
              ELSE { job_socket^.connection_id = nac$null_connection_id
                osp$set_status_condition (nae$sk_socket_not_connected, status);
              IFEND;
              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
            IFEND;
          ELSE
            nlp$sk_unlock_job_socket (socket_id);
            IF job_socket^.status = nac$sk_socket_unbound THEN
              osp$set_status_condition (nae$sk_unbound_socket, status);
            ELSEIF job_socket^.status = nac$sk_socket_disconnected THEN
              osp$set_status_condition (nae$sk_socket_disconnected, status);
            ELSEIF job_socket^.status = nac$sk_socket_closed_via_peer THEN
              osp$set_status_condition (nae$sk_socket_closed_via_peer, status);
            ELSEIF job_socket^.status = nac$sk_socket_terminated THEN
              osp$set_status_condition (nae$sk_socket_terminated, status);
            ELSEIF job_socket^.status = nac$sk_job_recovery THEN
              osp$set_status_condition (nae$sk_job_recovery, status);
            IFEND;
            osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
          IFEND;
        ELSE { Incorrect socket type
          nlp$sk_unlock_job_socket (socket_id);
          osp$set_status_abnormal (nac$status_id, nae$sk_incorrect_socket_type, 'READ SOCKET', status);
        IFEND;
      ELSE { Invalid user
        nlp$sk_unlock_job_socket (socket_id);
        osp$set_status_abnormal (nac$status_id, nae$sk_invalid_user, 'NAP$SK_READ_SOCKET', status);
      IFEND;
    ELSE { Unknown socket
      nlp$sk_unlock_job_socket (socket_id);
      osp$set_status_condition (nae$sk_unknown_socket, status);
      osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
    IFEND;
    osp$disestablish_cond_handler;
    osp$pop_inhibit_job_recovery;

  PROCEND nap$sk_read_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$sk_receive_from_socket', EJECT ??
*copy nah$sk_receive_from_socket

  PROCEDURE [XDCL, #GATE] nap$sk_receive_from_socket
    (    socket_id: nat$sk_socket_identifier;
         selection_criteria: ^nat$sk_socket_address;
     VAR foreign_socket: nat$sk_socket_address;
     VAR local_ip_address: ^nat$sk_ip_address;
         data { input, output } : nat$data_fragments;
     VAR data_length: integer;
     VAR status: ost$status);

    VAR
      caller_id: ost$caller_identifier,
      global_socket_id: nlt$udp_global_socket_id,
      interface_mode: nat$sk_interface_mode,
      interface_timeout: nat$wait_time,
      job_socket: ^nat$sk_job_socket,
      local_address: nat$sk_ip_address,
      local_ip_address_enabled: boolean,
      time_stamp: ost$free_running_clock,
      user_cache_enabled: boolean,
      user_selection_criteria: nat$sk_socket_address;

    status.normal := TRUE;
    osp$push_inhibit_job_recovery;
    IF socket_id > 0 THEN
      nlp$sk_lock_job_socket (socket_id, job_socket);
      IF job_socket <> NIL THEN
        #CALLER_ID (caller_id);
        IF caller_id.ring <= job_socket^.ring THEN
          IF job_socket^.socket_type = nac$sk_udp_socket THEN
            IF job_socket^.status = nac$sk_socket_open THEN
              interface_mode := job_socket^.interface_mode;
              IF interface_mode = nac$sk_blocking_mode THEN
                interface_timeout := job_socket^.interface_timeout;
              ELSE
                interface_timeout := 0;
              IFEND;
              global_socket_id := job_socket^.global_socket_id;
              local_ip_address_enabled := job_socket^.local_ip_address_enabled;
              user_cache_enabled := job_socket^.user_cache_enabled;
              time_stamp := job_socket^.time_stamp;
              nlp$sk_unlock_job_socket (socket_id);
              IF selection_criteria <> NIL THEN
                user_selection_criteria := selection_criteria^;
              ELSE
                user_selection_criteria.ip_address := nac$sk_all_ip_addresses;
                user_selection_criteria.port := 0;
              IFEND;

              nlp$udp_receive_data (global_socket_id, time_stamp, user_selection_criteria, user_cache_enabled,
                    interface_mode, interface_timeout, data, foreign_socket, local_address, data_length,
                    status);
              IF status.normal THEN
                IF local_ip_address_enabled AND (local_ip_address <> NIL) THEN
                  local_ip_address^ := local_address;
                IFEND;
              ELSEIF status.condition = nae$sk_socket_terminated THEN
                nlp$sk_lock_job_socket (socket_id, job_socket);
                IF (job_socket <> NIL) AND (job_socket^.time_stamp = time_stamp) THEN
                  nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                IFEND;
                nlp$sk_unlock_job_socket (socket_id);
                osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
              ELSEIF status.condition = nae$sk_unknown_socket THEN
                osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
              IFEND;
            ELSE
              nlp$sk_unlock_job_socket (socket_id);
              IF job_socket^.status = nac$sk_socket_unbound THEN
                osp$set_status_condition (nae$sk_unbound_socket, status);
              ELSEIF job_socket^.status = nac$sk_socket_terminated THEN
                osp$set_status_condition (nae$sk_socket_terminated, status);
              ELSEIF job_socket^.status = nac$sk_job_recovery THEN
                osp$set_status_condition (nae$sk_job_recovery, status);
              IFEND;
              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
            IFEND;
          ELSE { Incorrect socket type
            nlp$sk_unlock_job_socket (socket_id);
            osp$set_status_abnormal (nac$status_id, nae$sk_incorrect_socket_type, 'RECEIVE FROM SOCKET',
                  status);
          IFEND;
        ELSE { Invalid user
          nlp$sk_unlock_job_socket (socket_id);
          osp$set_status_abnormal (nac$status_id, nae$sk_invalid_user, 'NAP$SK_RECEIVE_FROM_SOCKET', status);
        IFEND;
      ELSE { Unknown socket id
        nlp$sk_unlock_job_socket (socket_id);
        osp$set_status_condition (nae$sk_unknown_socket, status);
        osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
      IFEND;
    ELSE { socket_id = 0
      osp$set_status_condition (nae$sk_unknown_socket, status);
      osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
    IFEND;
    osp$pop_inhibit_job_recovery;

  PROCEND nap$sk_receive_from_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$sk_send_to_socket', EJECT ??
*copy nah$sk_send_to_socket

  PROCEDURE [XDCL, #GATE] nap$sk_send_to_socket
    (    socket_id: nat$sk_socket_identifier;
         local_ip_address: ^nat$sk_ip_address;
         destination_socket: nat$sk_socket_address;
         data: nat$data_fragments;
     VAR status: ost$status);

?? NEWTITLE := 'terminate_send_to_socket', EJECT ??

    PROCEDURE terminate_send_to_socket
      (    condition: pmt$condition;
           ignore_condition_descriptor: ^pmt$condition_information;
           sa: ^ost$stack_frame_save_area;
       VAR condition_status: ost$status);

      nap$condition_handler_trace (condition, sa);
      CASE condition.selector OF
      = pmc$system_conditions, mmc$segment_access_condition =
        nlp$sk_clear_job_socket_lock (socket_id);
        osp$pop_inhibit_job_recovery;
        osp$set_status_from_condition (nac$status_id, condition, sa, status, condition_status);
        condition_status.normal := TRUE;
        EXIT nap$sk_send_to_socket; {----->
      = pmc$user_defined_condition =
        IF condition.user_condition_name = 'OSC$JOB_RECOVERY' THEN
          pmp$continue_to_cause (pmc$execute_standard_procedure, condition_status);
        IFEND;
        condition_status.normal := TRUE;
      ELSE
        condition_status.normal := TRUE;
      CASEND;

    PROCEND terminate_send_to_socket;
?? OLDTITLE ??
?? EJECT ??

    VAR
      caller_id: ost$caller_identifier,
      checksum: boolean,
      data_length: nat$data_length,
      destination_address: nat$sk_socket_address,
      global_socket_id: nlt$udp_global_socket_id,
      interface_mode: nat$sk_interface_mode,
      interface_timeout: nat$wait_time,
      job_socket: ^nat$sk_job_socket,
      local_address: nat$sk_ip_address,
      time_stamp: ost$free_running_clock,
      user_cache_enabled: boolean;

    status.normal := TRUE;
    osp$push_inhibit_job_recovery;
    osp$establish_condition_handler (^terminate_send_to_socket, FALSE);
    IF socket_id > 0 THEN
      nlp$sk_lock_job_socket (socket_id, job_socket);
      IF job_socket <> NIL THEN
        #CALLER_ID (caller_id);
        IF caller_id.ring <= job_socket^.ring THEN
          IF job_socket^.socket_type = nac$sk_udp_socket THEN
            IF (job_socket^.status = nac$sk_socket_open) OR (job_socket^.status = nac$sk_socket_unbound) THEN

{ Check that the user is trying to send > 9000 bytes with a non-blocking interface mode.

              nlp$al_get_data_length (data, data_length);
              IF (data_length < nac$sk_max_nonblocked_data_size) OR
                    (job_socket^.interface_mode = nac$sk_blocking_mode) THEN
                interface_mode := job_socket^.interface_mode;
                IF interface_mode = nac$sk_blocking_mode THEN
                  interface_timeout := job_socket^.interface_timeout;
                ELSE
                  interface_timeout := 0;
                IFEND;
                IF (job_socket^.local_ip_address_enabled) AND (local_ip_address <> NIL) THEN
                  local_address := local_ip_address^;
                ELSE
                  local_address := job_socket^.bound_address;
                IFEND;
                IF destination_socket.ip_address <> loopback_address THEN
                  destination_address := destination_socket;
                ELSE { loopback ... substitute local address for destination.
                  IF nlv$tm_device_configuration^.udp.count > 0 THEN
                    local_address := nlv$tm_device_configuration^.
                          list [nlv$tm_device_configuration^.udp.identifier].local_device_address.full;
                    destination_address.ip_address := local_address;
                    destination_address.port := destination_socket.port;
                  ELSE { No local address ... send will fail with no device available message.
                    destination_address := destination_socket;
                  IFEND;
                IFEND;
                global_socket_id := job_socket^.global_socket_id;
                user_cache_enabled := job_socket^.user_cache_enabled;
                checksum := job_socket^.checksum;
                time_stamp := job_socket^.time_stamp;
                IF job_socket^.status = nac$sk_socket_unbound THEN
                  bind_udp_socket (job_socket, job_socket^.port, nac$sk_all_ip_addresses, status);
                IFEND;
                IF status.normal THEN { socket is open and bound
                  nlp$sk_unlock_job_socket (socket_id);
                  nlp$udp_send_data (global_socket_id, time_stamp, local_address, destination_address, data,
                        data_length, checksum, interface_mode, interface_timeout, user_cache_enabled, status);
                  IF NOT status.normal THEN
                    IF status.condition = nae$sk_socket_terminated THEN
                      nlp$sk_lock_job_socket (socket_id, job_socket);
                      IF (job_socket <> NIL) AND (job_socket^.time_stamp = time_stamp) THEN
                        nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                      IFEND;
                      nlp$sk_unlock_job_socket (socket_id);
                      osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                    ELSEIF status.condition = nae$sk_unknown_socket THEN
                      osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                    IFEND;
                  IFEND;
                ELSE { NOT status.normal from bind_socket request.
                  IF status.condition = nae$sk_socket_terminated THEN
                    nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                  IFEND;
                  nlp$sk_unlock_job_socket (socket_id);
                IFEND;
              ELSE
                nlp$sk_unlock_job_socket (socket_id);
                osp$set_status_abnormal (nac$status_id, nae$sk_max_nonblock_size_exceed, 'SEND TO SOCKET',
                      status);
              IFEND;
            ELSE
              nlp$sk_unlock_job_socket (socket_id);
              IF job_socket^.status = nac$sk_socket_terminated THEN
                osp$set_status_condition (nae$sk_socket_terminated, status);
              ELSEIF job_socket^.status = nac$sk_job_recovery THEN
                osp$set_status_condition (nae$sk_job_recovery, status);
              IFEND;
              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
            IFEND;
          ELSE
            nlp$sk_unlock_job_socket (socket_id);
            osp$set_status_abnormal (nac$status_id, nae$sk_incorrect_socket_type, 'SEND TO SOCKET', status);
          IFEND;
        ELSE { invalid user
          nlp$sk_unlock_job_socket (socket_id);
          osp$set_status_abnormal (nac$status_id, nae$sk_invalid_user, 'NAP$SK_SEND_TO_SOCKET', status);
        IFEND;
      ELSE
        nlp$sk_unlock_job_socket (socket_id);
        osp$set_status_condition (nae$sk_unknown_socket, status);
        osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
      IFEND;
    ELSE { socket_id = 0
      osp$set_status_condition (nae$sk_unknown_socket, status);
      osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
    IFEND;
    osp$pop_inhibit_job_recovery;

  PROCEND nap$sk_send_to_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$sk_set_socket_options', EJECT ??
*copy nah$sk_set_socket_options

  PROCEDURE [XDCL, #GATE] nap$sk_set_socket_options
    (    socket_id: nat$sk_socket_identifier;
         options: nat$sk_socket_options;
     VAR status: ost$status);

    VAR
      caller_id: ost$caller_identifier,
      i: integer,
      job_socket: ^nat$sk_job_socket,
      old_job_socket: nat$sk_job_socket;

    status.normal := TRUE;
    osp$push_inhibit_job_recovery;
    IF socket_id > 0 THEN
      nlp$sk_lock_job_socket (socket_id, job_socket);
      IF job_socket <> NIL THEN
        #CALLER_ID (caller_id);
        IF caller_id.ring <= job_socket^.ring THEN
          IF (job_socket^.status = nac$sk_socket_open) OR (job_socket^.status = nac$sk_socket_unbound) THEN

{ Verify all socket options.

            verify_socket_options (job_socket, options, status);
            IF status.normal THEN
              old_job_socket := job_socket^;

{ Store the socket options in the job socket.

              nlp$sk_update_socket_options (job_socket^.identifier, options);
              IF job_socket^.socket_type = nac$sk_udp_socket THEN
                IF (job_socket^.traffic_pattern <> old_job_socket.traffic_pattern) OR
                      (job_socket^.broadcast_enabled <> old_job_socket.broadcast_enabled) THEN
                  nlp$udp_set_socket_options (job_socket^.global_socket_id, job_socket^.traffic_pattern,
                        job_socket^.broadcast_enabled, status);
                  IF (NOT status.normal) AND (status.condition = nae$sk_socket_terminated) THEN
                    nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                    osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                  IFEND;
                IFEND;
              ELSEIF job_socket^.socket_type = nac$sk_tcp_socket THEN
                IF job_socket^.tcp_socket_type <> nlc$tcp_null_socket THEN
                  IF ((job_socket^.graceful_close <> old_job_socket.graceful_close) OR
                        (job_socket^.traffic_pattern <> old_job_socket.traffic_pattern)) AND
                        (job_socket^.connection_id <> nac$null_connection_id) THEN
                    nlp$sk_tcp_set_socket_options (job_socket^.connection_id, job_socket^.graceful_close,
                          job_socket^.traffic_pattern, status);
                    IF NOT status.normal THEN
                      IF status.condition = nae$sk_socket_disconnected THEN
                        nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_disconnected);
                      ELSEIF status.condition = nae$sk_socket_closed_via_peer THEN
                        nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_closed_via_peer);
                      ELSEIF status.condition = nae$sk_socket_terminated THEN
                        nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                        osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                              status);
                      IFEND;
                    IFEND;
                  IFEND;
                IFEND;
              IFEND;
            IFEND;
          ELSEIF job_socket^.status = nac$sk_socket_disconnected THEN
            osp$set_status_condition (nae$sk_socket_disconnected, status);
            osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
          ELSEIF job_socket^.status = nac$sk_socket_closed_via_peer THEN
            osp$set_status_condition (nae$sk_socket_closed_via_peer, status);
            osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
          ELSEIF job_socket^.status = nac$sk_socket_terminated THEN
            osp$set_status_condition (nae$sk_socket_terminated, status);
            osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
          ELSEIF job_socket^.status = nac$sk_job_recovery THEN
            osp$set_status_condition (nae$sk_job_recovery, status);
            osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
          IFEND;
        ELSE { invalid user
          osp$set_status_abnormal (nac$status_id, nae$sk_invalid_user, 'NAP$SK_SET_SOCKET_OPTIONS', status);
        IFEND;
      ELSE { job_socket = NIL
        osp$set_status_condition (nae$sk_unknown_socket, status);
        osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
      IFEND;
      nlp$sk_unlock_job_socket (socket_id);
    ELSE { socket_id = 0
      osp$set_status_condition (nae$sk_unknown_socket, status);
      osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
    IFEND;
    osp$pop_inhibit_job_recovery;

  PROCEND nap$sk_set_socket_options;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nap$sk_write_socket', EJECT ??
*copy nah$sk_write_socket

  PROCEDURE [XDCL, #GATE] nap$sk_write_socket
    (    socket_id: nat$sk_socket_identifier;
         urgent_flag: boolean;
         push_flag: boolean;
         data: nat$data_fragments;
     VAR data_transferred: integer;
     VAR status: ost$status);

?? NEWTITLE := 'terminate_write_socket', EJECT ??

    PROCEDURE terminate_write_socket
      (    condition: pmt$condition;
           ignore_condition_descriptor: ^pmt$condition_information;
           sa: ^ost$stack_frame_save_area;
       VAR condition_status: ost$status);

      nap$condition_handler_trace (condition, sa);
      CASE condition.selector OF
      = pmc$system_conditions, mmc$segment_access_condition =
        IF cl_connection <> NIL THEN
          nlp$cl_clear_exclusive_access (cl_connection);
        IFEND;
        IF job_socket <> NIL THEN
          nlp$sk_unlock_job_socket (socket_id);
        IFEND;
        osp$pop_inhibit_job_recovery;
        osp$set_status_from_condition (nac$status_id, condition, sa, status, condition_status);
        condition_status.normal := TRUE;
        EXIT nap$sk_write_socket; {----->
      = pmc$user_defined_condition =
        IF condition.user_condition_name = 'OSC$JOB_RECOVERY' THEN
          pmp$continue_to_cause (pmc$execute_standard_procedure, condition_status);
        IFEND;
        condition_status.normal := TRUE;
      = pmc$block_exit_processing =
        nlp$cl_get_exclusive_via_cid (connection_id, connection_exists, cl_connection);
        IF cl_connection <> NIL THEN
          nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active, tcp_connection);
          IF layer_active THEN

{ Find the sender task for the current task_id.

            sender_task := tcp_connection^.send_queue;
            previous_sender_task := ^tcp_connection^.send_queue;
            WHILE (sender_task <> NIL) AND (sender_task^.task_id <> current_task_id) DO
              previous_sender_task := ^sender_task^.next_entry;
              sender_task := sender_task^.next_entry;
            WHILEND;
            IF sender_task <> NIL THEN
              previous_sender_task^ := sender_task^.next_entry;
              nlp$sk_tcp_ret_send_task_entry (tcp_connection, sender_task);
            IFEND;
          IFEND;
          nlp$cl_release_exclusive_access (cl_connection);
        IFEND;
        condition_status.normal := TRUE;
      ELSE

{ Note: Interactive condition is being ignored.

        condition_status.normal := TRUE;
      CASEND;

    PROCEND terminate_write_socket;
?? OLDTITLE ??
?? EJECT ??

    VAR
      activity_complete: boolean,
      caller_id: ost$caller_identifier,
      capacity: nat$data_length,
      cl_connection: ^nlt$cl_connection,
      connection_exists: boolean,
      connection_id: nat$connection_id,
      current_lowerbound: nat$data_fragment_count,
      current_task_id: ost$global_task_id,
      current_time: ost$free_running_clock,
      data_fragments: ^nat$data_fragments,
      data_length: nat$data_length,
      data_length_to_be_sent: nat$data_length,
      end_time: integer,
      interface_timeout: nat$wait_time,
      job_socket: ^nat$sk_job_socket,
      layer_active: boolean,
      new_lowerbound: nat$data_fragment_count,
      previous_sender_task: ^^nlt$tcp_sender_task,
      remaining_data_length: integer,
      remaining_time: integer,
      sender_active: boolean,
      sender_task: ^nlt$tcp_sender_task,
      tcp_connection: ^nlt$tcp_socket_layer;

    IF socket_id = 0 THEN
      osp$set_status_condition (nae$sk_unknown_socket, status);
      osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
      RETURN; {----->
    IFEND;

    status.normal := TRUE;
    cl_connection := NIL;
    #SPOIL (cl_connection);
    pmp$get_executing_task_gtid (current_task_id);
    #SPOIL (current_task_id);
    job_socket := NIL;
    #SPOIL (job_socket);
    osp$push_inhibit_job_recovery;
    osp$establish_condition_handler (^terminate_write_socket, FALSE);

    nlp$sk_lock_job_socket (socket_id, job_socket);
    #SPOIL (job_socket);
    activity_complete := FALSE;
    data_transferred := 0;
    #SPOIL (data_transferred);
    IF (job_socket <> NIL) THEN
      #CALLER_ID (caller_id);
      IF caller_id.ring <= job_socket^.ring THEN
        IF job_socket^.socket_type = nac$sk_tcp_socket THEN
          IF job_socket^.status = nac$sk_socket_open THEN
            IF job_socket^.connection_id <> nac$null_connection_id THEN
              nlp$cl_get_exclusive_via_cid (job_socket^.connection_id, connection_exists, cl_connection);
              #SPOIL (cl_connection);
              sender_active := FALSE;
              IF cl_connection <> NIL THEN
                nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active, tcp_connection);
                IF layer_active THEN
                  IF tcp_connection^.state = nlc$tcp_conn_open THEN
                    connection_id := job_socket^.connection_id;
                    #SPOIL (connection_id);
                    interface_timeout := job_socket^.interface_timeout;

{ Setup the data fragments on the local stack.

                    PUSH data_fragments: [1 .. UPPERBOUND (data)];
                    data_fragments^ := data;
                    nlp$al_get_data_length (data, data_length);
                    IF (data_length > 0) OR ((data_length = 0) AND push_flag) THEN
                      IF tcp_connection^.send_queue = NIL THEN
                        nlp$osi_get_outbound_capacity (cl_connection, capacity);
                        IF capacity <= 0 THEN
                          nlp$cc_receive_data (cl_connection);
                          nlp$osi_get_outbound_capacity (cl_connection, capacity);
                        IFEND;
                        IF capacity > 0 THEN
                          nlp$sk_tcp_send_data (cl_connection, capacity, data_fragments^, data_length,
                                push_flag, urgent_flag, 1, new_lowerbound, remaining_data_length);
                          activity_complete := remaining_data_length = 0;
                          data_transferred := data_length - remaining_data_length;
                          #SPOIL (data_transferred);
                          IF NOT activity_complete THEN
                            IF job_socket^.interface_mode = nac$sk_blocking_mode THEN
                              nlp$sk_tcp_get_send_task_entry (tcp_connection, sender_task);
                              sender_task^.next_entry := NIL;
                              sender_task^.task_id := current_task_id;
                              sender_task^.send_type := nlc$tcp_send_data;
                              data_length_to_be_sent := remaining_data_length;
                              current_lowerbound := new_lowerbound;
                              tcp_connection^.send_queue := sender_task;
                              nlp$cl_activate_sender (cl_connection);
                              sender_active := TRUE;
                            ELSE { non-blocking mode
                              activity_complete := TRUE;
                            IFEND;
                          IFEND;
                        ELSE { capacity = 0
                          IF job_socket^.interface_mode = nac$sk_blocking_mode THEN
                            nlp$sk_tcp_get_send_task_entry (tcp_connection, sender_task);
                            sender_task^.next_entry := NIL;
                            sender_task^.task_id := current_task_id;
                            sender_task^.send_type := nlc$tcp_send_data;
                            data_length_to_be_sent := data_length;
                            current_lowerbound := 1;
                            tcp_connection^.send_queue := sender_task;
                            nlp$cl_activate_sender (cl_connection);
                            sender_active := TRUE;
                          ELSE { Non-blocking mode
                            osp$set_status_abnormal (nac$status_id, nae$sk_insufficient_resources,
                                  'TCP Write', status);
                          IFEND;
                        IFEND;
                      ELSE { tcp_connection^.send_queue <> NIL
                        IF job_socket^.interface_mode = nac$sk_blocking_mode THEN
                          nlp$sk_tcp_get_send_task_entry (tcp_connection, sender_task);
                          sender_task^.next_entry := NIL;
                          sender_task^.task_id := current_task_id;
                          sender_task^.send_type := nlc$tcp_send_data;
                          data_length_to_be_sent := data_length;
                          current_lowerbound := 1;

{ Queue the task at the end of the send queue.

                          previous_sender_task := ^tcp_connection^.send_queue;
                          WHILE previous_sender_task^ <> NIL DO
                            previous_sender_task := ^previous_sender_task^^.next_entry;
                          WHILEND;
                          previous_sender_task^ := sender_task;
                        ELSE { Non-blocking mode
                          osp$set_status_condition (nae$sk_write_in_progress, status);
                          osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                                status);
                        IFEND;
                      IFEND;
                    ELSE { data_length = 0 AND NOT push_flag
                      osp$set_status_condition (nae$sk_zero_length_data, status);
                    IFEND;
                  ELSEIF tcp_connection^.state = nlc$tcp_conn_closed THEN
                    IF tcp_connection^.disconnect_reason = nlc$tcpaa_ri_peer_termination THEN
                      osp$set_status_condition (nae$sk_socket_closed_via_peer, status);
                      nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_closed_via_peer);
                    ELSE
                      osp$set_status_condition (nae$sk_socket_disconnected, status);
                      nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_disconnected);
                    IFEND;
                    osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                    IF (tcp_connection^.send_queue = NIL) AND (tcp_connection^.receive_queue = NIL) THEN
                      nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
                    IFEND;
                  ELSEIF tcp_connection^.state = nlc$tcp_conn_closing THEN
                    IF tcp_connection^.disconnect_reason = nlc$tcpaa_ri_peer_termination THEN
                      osp$set_status_condition (nae$sk_socket_closed_via_peer, status);
                    ELSE
                      osp$set_status_condition (nae$sk_socket_disconnected, status);
                    IFEND;
                    osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                  ELSEIF tcp_connection^.state = nlc$tcp_conn_terminated THEN
                    nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                    IF (tcp_connection^.send_queue = NIL) AND (tcp_connection^.receive_queue = NIL) THEN
                      nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
                    IFEND;
                    osp$set_status_condition (nae$sk_socket_terminated, status);
                    osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                  ELSE { all other states
                    nap$namve_system_error ({Recoverable_error=} TRUE, 'Unexpected socket layer state', NIL);
                    osp$set_status_abnormal (nac$status_id, nae$sk_internal_error, 'WRITE SOCKET', status);
                  IFEND;
                ELSE { Layer inactive

{ Is is assumed that the socket has been terminated via application management.

                  nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                  osp$set_status_condition (nae$sk_socket_terminated, status);
                  osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
                IFEND;
                nlp$cl_release_exclusive_access (cl_connection);
                cl_connection := NIL;
                #SPOIL (cl_connection);
              ELSE { cl_connection = NIL
                nlp$sk_update_job_socket_status (socket_id, nac$sk_socket_terminated);
                osp$set_status_condition (nae$sk_socket_terminated, status);
                osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
              IFEND;

              nlp$sk_unlock_job_socket (socket_id);
              job_socket := NIL;
              #SPOIL (job_socket);
              IF (NOT activity_complete) AND (status.normal) THEN

{ Unable to send any or all data.

                end_time := #FREE_RUNNING_CLOCK (0) + interface_timeout * 1000;
                remaining_time := interface_timeout;
                REPEAT
                  pmp$wait (remaining_time, 0);
                  current_time := #FREE_RUNNING_CLOCK (0);
                  IF end_time > current_time THEN
                    remaining_time := (end_time - current_time) DIV 1000;
                  ELSE
                    remaining_time := 0;
                  IFEND;

                  nlp$cl_get_exclusive_via_cid (connection_id, connection_exists, cl_connection);
                  #SPOIL (cl_connection);
                  IF cl_connection <> NIL THEN
                    nlp$cl_get_layer_connection (nlc$tcp_interface, cl_connection, layer_active,
                          tcp_connection);
                    IF layer_active THEN
                      IF tcp_connection^.state = nlc$tcp_conn_open THEN
                        sender_task := tcp_connection^.send_queue;
                        IF sender_task^.task_id = current_task_id THEN

{ The current task is at the head of the send queue.

                          IF NOT sender_active THEN
                            nlp$cl_activate_sender (cl_connection);
                            sender_active := TRUE;
                          IFEND;
                          nlp$cc_receive_data (cl_connection);
                          nlp$osi_get_outbound_capacity (cl_connection, capacity);
                          IF capacity > 0 THEN
                            nlp$sk_tcp_send_data (cl_connection, capacity, data_fragments^,
                                  data_length_to_be_sent, push_flag, urgent_flag, current_lowerbound,
                                  new_lowerbound, remaining_data_length);
                            data_transferred := data_length - remaining_data_length;
                            #SPOIL (data_transferred);
                            IF (remaining_data_length = 0) OR (remaining_time = 0) THEN
                              activity_complete := TRUE;
                              tcp_connection^.send_queue := sender_task^.next_entry;
                              nlp$sk_tcp_ret_send_task_entry (tcp_connection, sender_task);
                              nlp$cl_deactivate_sender (cl_connection);

{ The following needs to be done to process the clear to send indication that
{ may have been queued on the connection.

                              nlp$cc_receive_data (cl_connection);
                              IF tcp_connection^.send_queue <> NIL THEN
                                pmp$ready_task (tcp_connection^.send_queue^.task_id, {ignore} status);
                                status.normal := TRUE;
                              IFEND;
                            ELSE { more data to be sent
                              data_length_to_be_sent := remaining_data_length;
                              current_lowerbound := new_lowerbound;
                            IFEND;
                          ELSE { capacity < = 0
                            IF remaining_time = 0 THEN
                              activity_complete := TRUE;
                              tcp_connection^.send_queue := sender_task^.next_entry;
                              nlp$sk_tcp_ret_send_task_entry (tcp_connection, sender_task);
                              nlp$cl_deactivate_sender (cl_connection);

{ The following needs to be done to process the clear to send indication that
{ may have been queued on the connection.

                              nlp$cc_receive_data (cl_connection);
                              IF tcp_connection^.send_queue <> NIL THEN
                                pmp$ready_task (tcp_connection^.send_queue^.task_id, {ignore} status);
                                status.normal := TRUE;
                              IFEND;
                              IF data_transferred = 0 THEN
                                osp$set_status_condition (nae$sk_interface_timeout, status);
                                osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10,
                                      TRUE, status);
                              IFEND;
                            IFEND;
                          IFEND;
                        ELSE { current task not at the head of the queue
                          IF remaining_time = 0 THEN

{ Dequeue the sender task.

                            previous_sender_task := ^tcp_connection^.send_queue;
                            WHILE (sender_task <> NIL) AND (sender_task^.task_id <> current_task_id) DO
                              previous_sender_task := ^sender_task^.next_entry;
                              sender_task := sender_task^.next_entry;
                            WHILEND;
                            IF sender_task <> NIL THEN
                              previous_sender_task^ := sender_task^.next_entry;
                              nlp$sk_tcp_ret_send_task_entry (tcp_connection, sender_task);
                            IFEND;
                            osp$set_status_condition (nae$sk_interface_timeout, status);
                            osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                                  status);
                          IFEND;
                        IFEND;
                      ELSE { tcp_connection^.state <> nlc$tcp_conn_open
                        activity_complete := TRUE;
                        previous_sender_task := ^tcp_connection^.send_queue;
                        WHILE (sender_task <> NIL) AND (sender_task^.task_id <> current_task_id) DO
                          previous_sender_task := ^sender_task^.next_entry;
                          sender_task := sender_task^.next_entry;
                        WHILEND;
                        IF sender_task <> NIL THEN
                          previous_sender_task^ := sender_task^.next_entry;
                          nlp$sk_tcp_ret_send_task_entry (tcp_connection, sender_task);
                        IFEND;
                        IF tcp_connection^.state = nlc$tcp_conn_closed THEN
                          IF tcp_connection^.user_initiated_close THEN
                            IF (tcp_connection^.send_queue = NIL) AND
                                  (tcp_connection^.receive_queue = NIL) THEN
                              nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
                            IFEND;
                            IF data_transferred = 0 THEN
                              osp$set_status_condition (nae$sk_unknown_socket, status);
                              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                                    status);
                            IFEND;
                          ELSE { NOT tcp_connection^.user_initiated_close
                            IF data_transferred = 0 THEN
                              osp$set_status_condition (nae$sk_socket_disconnected, status);
                              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                                    status);
                            IFEND;
                          IFEND;
                        ELSEIF tcp_connection^.state = nlc$tcp_conn_closing THEN
                          IF data_transferred = 0 THEN
                            osp$set_status_condition (nae$sk_socket_disconnected, status);
                            osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                                  status);
                          IFEND;
                        ELSEIF tcp_connection^.state = nlc$tcp_conn_terminated THEN
                          IF (tcp_connection^.send_queue = NIL) AND (tcp_connection^.receive_queue = NIL) THEN
                            nlp$sk_tcp_deactivate_layer (cl_connection, tcp_connection);
                          IFEND;
                          IF data_transferred = 0 THEN
                            osp$set_status_condition (nae$sk_socket_terminated, status);
                            osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE,
                                  status);
                          IFEND;
                        IFEND;
                      IFEND;
                    ELSE { layer inactive
                      nap$namve_system_error ({Recoverable_error=} TRUE, 'TCP socket layer inactive', NIL);
                      osp$set_status_abnormal (nac$status_id, nae$sk_internal_error, 'WRITE SOCKET', status);
                    IFEND;
                    nlp$cl_release_exclusive_access (cl_connection);
                    cl_connection := NIL;
                    #SPOIL (cl_connection);
                  ELSE { cl_connection = NIL
                    nap$namve_system_error ({Recoverable_error=} TRUE, 'CL connection NIL', NIL);
                    osp$set_status_abnormal (nac$status_id, nae$sk_internal_error, 'WRITE SOCKET', status);
                  IFEND;
                UNTIL (activity_complete) OR (NOT status.normal);
              IFEND;
            ELSE
              nlp$sk_unlock_job_socket (socket_id);
              IF job_socket^.tcp_socket_type = nlc$tcp_listen_socket THEN
                osp$set_status_condition (nae$sk_listen_already_active, status);
              ELSE { job_socket^.connection_id = nac$null_connection_id
                osp$set_status_condition (nae$sk_socket_not_connected, status);
              IFEND;
              osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
            IFEND;
          ELSE
            nlp$sk_unlock_job_socket (socket_id);
            IF job_socket^.status = nac$sk_socket_unbound THEN
              osp$set_status_condition (nae$sk_unbound_socket, status);
            ELSEIF job_socket^.status = nac$sk_socket_disconnected THEN
              osp$set_status_condition (nae$sk_socket_disconnected, status);
            ELSEIF job_socket^.status = nac$sk_socket_closed_via_peer THEN
              osp$set_status_condition (nae$sk_socket_closed_via_peer, status);
            ELSEIF job_socket^.status = nac$sk_socket_terminated THEN
              osp$set_status_condition (nae$sk_socket_terminated, status);
            ELSEIF job_socket^.status = nac$sk_job_recovery THEN
              osp$set_status_condition (nae$sk_job_recovery, status);
            IFEND;
            osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
          IFEND;
        ELSE { Incorrect socket type
          nlp$sk_unlock_job_socket (socket_id);
          osp$set_status_abnormal (nac$status_id, nae$sk_incorrect_socket_type, 'WRITE SOCKET', status);
        IFEND;
      ELSE { Invalid user
        nlp$sk_unlock_job_socket (socket_id);
        osp$set_status_abnormal (nac$status_id, nae$sk_invalid_user, 'NAP$SK_WRITE_SOCKET', status);
      IFEND;
    ELSE { Unknown socket
      nlp$sk_unlock_job_socket (socket_id);
      osp$set_status_condition (nae$sk_unknown_socket, status);
      osp$append_status_integer (osc$status_parameter_delimiter, socket_id, 10, TRUE, status);
    IFEND;

    osp$disestablish_cond_handler;
    osp$pop_inhibit_job_recovery;

  PROCEND nap$sk_write_socket;
?? OLDTITLE ??
?? NEWTITLE := 'bind_tcp_socket', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to bind the given TCP socket to
{ the specified port and IP address. If the port number is 0, the
{ socket is bound to a port number assigned by the socket layer.
{ If a 0 value is specified for the IP address, the socket is bound
{ to all known IP addresses.

  PROCEDURE [INLINE] bind_tcp_socket
    (    job_socket { input, output } : ^nat$sk_job_socket;
         port: nat$sk_port_number;
         ip_address: nat$sk_ip_address;
     VAR status: ost$status);

    VAR
      assigned_port: nat$sk_port_number;

    IF port > 0 THEN
      open_specified_tcp_port (ip_address, port, job_socket^.reuse_address, status);
      IF status.normal THEN
        nlp$sk_update_job_socket (job_socket^.identifier, port, ip_address, nac$sk_socket_open);
      IFEND;
    ELSE { Assign port
      open_tcp_port (ip_address, assigned_port, status);
      IF status.normal THEN
        nlp$sk_update_job_socket (job_socket^.identifier, assigned_port, ip_address, nac$sk_socket_open);
      IFEND;
    IFEND;

  PROCEND bind_tcp_socket;
?? OLDTITLE ??
?? NEWTITLE := 'bind_udp_socket', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to bind the given socket to
{ the specified port and IP address. If the port number is 0, the
{ socket is bound to a port number assigned by the socket layer.
{ If a 0 value is specified for the IP address, the socket is bound
{ to all known IP addresses.

  PROCEDURE [INLINE] bind_udp_socket
    (    job_socket { input, output } : ^nat$sk_job_socket;
         port: nat$sk_port_number;
         ip_address: nat$sk_ip_address;
     VAR status: ost$status);

    VAR
      assigned_port: nat$sk_port_number;

    status.normal := TRUE;
    IF port > 0 THEN
      open_specified_udp_port (ip_address, port, status);
      IF status.normal THEN
        nlp$udp_bind_socket (job_socket^.global_socket_id, port, job_socket^.traffic_pattern, ip_address,
              status);
        IF status.normal THEN
          nlp$sk_update_job_socket (job_socket^.identifier, port, ip_address, nac$sk_socket_open);
        ELSE
          close_udp_port (ip_address, port);
        IFEND;
      IFEND;
    ELSE { Assign a port
      open_udp_port (ip_address, assigned_port, status);
      IF status.normal THEN
        nlp$udp_bind_socket (job_socket^.global_socket_id, assigned_port, job_socket^.traffic_pattern,
              ip_address, status);
        IF status.normal THEN
          nlp$sk_update_job_socket (job_socket^.identifier, assigned_port, ip_address, nac$sk_socket_open);
        ELSE
          close_udp_port (ip_address, assigned_port);
        IFEND;
      IFEND;
    IFEND;

  PROCEND bind_udp_socket;
?? OLDTITLE ??
?? NEWTITLE := 'close_socket', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to close the specified job socket and
{ free all associated structures.

  PROCEDURE close_socket
    (VAR job_socket: ^nat$sk_job_socket;
     VAR status: ost$status);

    VAR
      socket_id: nat$sk_socket_identifier;

    socket_id := job_socket^.identifier;
    IF job_socket^.status <> nac$sk_job_recovery THEN
      IF job_socket^.socket_type = nac$sk_udp_socket THEN
        nlp$tcpip_decrement_appl_access (job_socket^.application, job_socket^.global_socket_id,
              nac$null_connection_id, {ignore} status);
        status.normal := TRUE;
        IF job_socket^.status <> nac$sk_socket_unbound THEN
          nlp$udp_close_socket (job_socket^.global_socket_id, FALSE {terminate_via_application_mgmt} );
          IF job_socket^.port > 0 THEN
            close_udp_port (job_socket^.bound_address, job_socket^.port);
          IFEND;
        ELSE {job_socket^.status = nac$sk_socket_unbound THEN
          nlp$udp_delete_global_socket (job_socket^.global_socket_id);
        IFEND;
      ELSEIF job_socket^.socket_type = nac$sk_tcp_socket THEN
        IF job_socket^.tcp_socket_type = nlc$tcp_listen_socket THEN
          nlp$sk_tcp_terminate_listen (job_socket^.application, job_socket^.port, job_socket^.bound_address);
          close_tcp_port (job_socket^.bound_address, job_socket^.port, TRUE);
        ELSEIF job_socket^.tcp_socket_type <> nlc$tcp_null_socket THEN

{ Connect or accept socket.

          nlp$tcpip_decrement_appl_access (job_socket^.application, nlc$udp_null_global_socket_id,
                job_socket^.connection_id, {ignore} status);
          status.normal := TRUE;
          nlp$sk_tcp_close_socket (job_socket^.connection_id, job_socket^.graceful_close);
          IF job_socket^.tcp_socket_type = nlc$tcp_connect_socket THEN
            close_tcp_port (job_socket^.bound_address, job_socket^.port, FALSE);
          IFEND;
        ELSE { null TCP socket type

{ Delete the job socket.

          IF job_socket^.status = nac$sk_socket_open THEN
            close_tcp_port (job_socket^.bound_address, job_socket^.port, FALSE);
          IFEND;
        IFEND;
      IFEND;
    IFEND;
    nlp$sk_delete_job_socket (socket_id, job_socket);
    nlp$sk_free_socket_id (socket_id);

  PROCEND close_socket;
?? OLDTITLE ??
?? NEWTITLE := 'close_tcp_port', EJECT ??

  PROCEDURE close_tcp_port
    (    ip_address: nat$sk_ip_address;
         port: nat$sk_port_number;
         listen_active: boolean);

    VAR
      open_port: ^nlt$tcp_open_port,
      previous_open_port: ^^nlt$tcp_open_port;

    osp$set_job_signature_lock (nlv$tcp_ports.lock);
    previous_open_port := ^nlv$tcp_ports.open_ports;
    WHILE (previous_open_port^ <> NIL) AND ((previous_open_port^^.port <> port) OR
          (previous_open_port^^.ip_address <> ip_address)) DO
      previous_open_port := ^previous_open_port^^.next_entry;
    WHILEND;
    IF previous_open_port^ <> NIL THEN
      open_port := previous_open_port^;
      open_port^.count := open_port^.count - 1;
      IF open_port^.count = 0 THEN
        previous_open_port^ := open_port^.next_entry;
        FREE open_port IN nav$network_paged_heap^;
      ELSEIF listen_active THEN
        open_port^.listen_active := FALSE;
      IFEND;
    ELSE { port/IP address not open
      nap$namve_system_error ({Recoverable_error=} TRUE, 'The port, IP address pair is not open.', NIL);
    IFEND;
    osp$clear_job_signature_lock (nlv$tcp_ports.lock);

  PROCEND close_tcp_port;
?? OLDTITLE ??
?? NEWTITLE := 'close_udp_port', EJECT ??

  PROCEDURE close_udp_port
    (    ip_address: nat$sk_ip_address;
         port: nat$sk_port_number);

    VAR
      i: integer,
      open_port: ^nlt$udp_open_port,
      previous_open_port: ^^nlt$udp_open_port;

    osp$set_job_signature_lock (nlv$udp_ports.lock);
    previous_open_port := ^nlv$udp_ports.open_ports;
    WHILE (previous_open_port^ <> NIL) AND ((previous_open_port^^.port <> port) OR
          (previous_open_port^^.ip_address <> ip_address)) DO
      previous_open_port := ^previous_open_port^^.next_entry;
    WHILEND;
    IF previous_open_port^ <> NIL THEN
      open_port := previous_open_port^;
      previous_open_port^ := open_port^.next_entry;
      FREE open_port IN nav$network_paged_heap^;
    ELSE { port/IP address not open
      nap$namve_system_error ({Recoverable_error=} TRUE, 'The port, IP address pair is not open.', NIL);
    IFEND;
    osp$clear_job_signature_lock (nlv$udp_ports.lock);

  PROCEND close_udp_port;
?? OLDTITLE ??
?? NEWTITLE := 'initialize_global_socket', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to initialize the global
{ socket entry from the given job socket.

  PROCEDURE initialize_global_socket
    (    job_socket: nat$sk_job_socket;
     VAR global_socket: ^nlt$udp_global_socket);

    VAR
      i: integer;

    global_socket^.next_entry := NIL;
    global_socket^.time_stamp := job_socket.time_stamp;
    global_socket^.status := nlc$udp_global_socket_unbound;
    global_socket^.local_socket_id := job_socket.identifier;
    global_socket^.port := job_socket.port;
    global_socket^.traffic_pattern := job_socket.traffic_pattern;
    global_socket^.broadcast_enabled := job_socket.broadcast_enabled;
    global_socket^.bound_address := job_socket.bound_address;

    global_socket^.waiting_task_id.index := 0;
    global_socket^.last_receiving_device := 1;
    global_socket^.active_device_count := 0;

{ Initialize the receive queue.

    global_socket^.receive_wait_queue := NIL;

    FOR i := 1 TO UPPERBOUND (global_socket^.device_list) DO
      global_socket^.device_list [i].device_id := i;
      global_socket^.device_list [i].status := nlc$udp_device_closed;
      global_socket^.device_list [i].ip_address := nac$sk_all_ip_addresses;
      global_socket^.device_list [i].connection_id := nac$null_connection_id;
      global_socket^.device_list [i].discard_data := FALSE;
      global_socket^.device_list [i].received_messages := NIL;
      global_socket^.device_list [i].receiver_task := NIL;
    FOREND;

  PROCEND initialize_global_socket;
?? OLDTITLE ??
?? NEWTITLE := 'initialize_tcp_pools', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to initialize the pools of available
{ sender, receiver task entries and the pool of available received data
{ entries.

  PROCEDURE initialize_tcp_pools
    (VAR tcp_connection: ^nlt$tcp_socket_layer);

    VAR
      i: integer,
      previous_received_data: ^^nlt$tcp_received_data,
      previous_receiver_task: ^^nlt$tcp_receiver_task,
      previous_sender_task: ^^nlt$tcp_sender_task,
      received_data: ^nlt$tcp_received_data,
      receiver_task: ^nlt$tcp_receiver_task,
      sender_task: ^nlt$tcp_sender_task;

{ Initialize available sender pool.

    previous_sender_task := ^tcp_connection^.available_sender_pool;
    FOR i := 1 TO nlc$tcp_max_pool_size DO
      REPEAT
        ALLOCATE sender_task IN nav$network_paged_heap^;
        IF sender_task = NIL THEN
          syp$cycle;
        IFEND;
      UNTIL sender_task <> NIL;
      previous_sender_task^ := sender_task;
      previous_sender_task := ^sender_task^.next_entry;
    FOREND;
    previous_sender_task^ := NIL;

{ Initialize available receiver pool.

    previous_receiver_task := ^tcp_connection^.available_receiver_pool;
    FOR i := 1 TO nlc$tcp_max_pool_size DO
      REPEAT
        ALLOCATE receiver_task IN nav$network_paged_heap^;
        IF receiver_task = NIL THEN
          syp$cycle;
        IFEND;
      UNTIL receiver_task <> NIL;
      previous_receiver_task^ := receiver_task;
      previous_receiver_task := ^receiver_task^.next_entry;
    FOREND;
    previous_receiver_task^ := NIL;

{ Initialize available data pool.

    previous_received_data := ^tcp_connection^.available_data_pool;
    FOR i := 1 TO nlc$tcp_max_pool_size DO
      REPEAT
        ALLOCATE received_data IN nav$network_paged_heap^;
        IF received_data = NIL THEN
          syp$cycle;
        IFEND;
      UNTIL received_data <> NIL;
      previous_received_data^ := received_data;
      previous_received_data := ^received_data^.next_entry;
    FOREND;
    previous_received_data^ := NIL;

  PROCEND initialize_tcp_pools;
?? OLDTITLE ??
?? NEWTITLE := 'mark_listen_port', EJECT ??

  PROCEDURE [INLINE] mark_listen_port
    (    port: nat$sk_port_number;
         ip_address: nat$sk_ip_address;
     VAR status: ost$status);

    VAR
      open_port: ^nlt$tcp_open_port;

    status.normal := TRUE;
    osp$set_job_signature_lock (nlv$tcp_ports.lock);
    open_port := nlv$tcp_ports.open_ports;
    WHILE (open_port <> NIL) AND ((open_port^.port <> port) OR (open_port^.ip_address <> ip_address)) DO
      open_port := open_port^.next_entry;
    WHILEND;
    IF open_port <> NIL THEN
      IF NOT open_port^.listen_active THEN
        open_port^.listen_active := TRUE;
      ELSE
        osp$set_status_condition (nae$sk_listen_active, status);
        osp$append_status_integer (osc$status_parameter_delimiter, port, 10, FALSE, status);
      IFEND;
    ELSE { port/IP address not open
      nap$namve_system_error ({Recoverable_error=} TRUE, 'The port, IP address pair is not open.', NIL);
      osp$set_status_abnormal (nac$status_id, nae$sk_internal_error, 'MARK LISTEN PORT', status);
    IFEND;
    osp$clear_job_signature_lock (nlv$tcp_ports.lock);

  PROCEND mark_listen_port;
?? OLDTITLE ??
?? NEWTITLE := 'open_specified_tcp_port', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to open the specified TCP port
{ for the given IP address. If the specified port has already been
{ opened by another user for the given IP address and the reuse option
{ has not been specified or reuse option has been specified and listen
{ is active on the open port, an abnormal status is returned to the
{ caller. If the specified port has not been opened for the given IP
{ address, it is now opened.

  PROCEDURE open_specified_tcp_port
    (    ip_address: nat$sk_ip_address;
         port: nat$sk_port_number;
         reuse_address: boolean;
     VAR status: ost$status);

    VAR
      new_open_port: ^nlt$tcp_open_port,
      open_port: ^nlt$tcp_open_port,
      previous_open_port: ^^nlt$tcp_open_port,
      reusing_address: boolean;

    status.normal := TRUE;
    osp$set_job_signature_lock (nlv$tcp_ports.lock);

{ Check if the specified port number is open over the given IP address.

    reusing_address := FALSE;
    open_port := nlv$tcp_ports.open_ports;
    previous_open_port := ^nlv$tcp_ports.open_ports;

  /check_if_port_open/
    WHILE open_port <> NIL DO
      IF open_port^.port = port THEN
        IF open_port^.ip_address = ip_address THEN
          IF reuse_address THEN
            IF NOT open_port^.listen_active THEN
              open_port^.count := open_port^.count + 1;
              reusing_address := TRUE;
            ELSE
              osp$set_status_condition (nae$sk_listen_active, status);
              osp$append_status_integer (osc$status_parameter_delimiter, port, 10, FALSE, status);
            IFEND;
          ELSE
            osp$set_status_abnormal (nac$status_id, nae$sk_port_already_in_use, 'TCP', status);
            osp$append_status_integer (osc$status_parameter_delimiter, port, 10, FALSE, status);
          IFEND;
          EXIT /check_if_port_open/; {----->
        ELSEIF (open_port^.ip_address = nac$sk_all_ip_addresses) OR
              (ip_address = nac$sk_all_ip_addresses) THEN
          osp$set_status_abnormal (nac$status_id, nae$sk_port_already_in_use, 'TCP', status);
          osp$append_status_integer (osc$status_parameter_delimiter, port, 10, FALSE, status);
          EXIT /check_if_port_open/; {----->
        IFEND;
      IFEND;
      previous_open_port := ^open_port^.next_entry;
      open_port := open_port^.next_entry;
    WHILEND /check_if_port_open/;

    IF status.normal AND (NOT reusing_address) THEN

{ Specified port/IP_address has not been opened yet.

      REPEAT
        ALLOCATE new_open_port IN nav$network_paged_heap^;
        IF new_open_port = NIL THEN
          syp$cycle;
        IFEND;
      UNTIL new_open_port <> NIL;
      new_open_port^.next_entry := NIL;
      new_open_port^.port := port;
      new_open_port^.ip_address := ip_address;
      new_open_port^.count := 1;
      new_open_port^.listen_active := FALSE;
      previous_open_port^ := new_open_port;
    IFEND;
    osp$clear_job_signature_lock (nlv$tcp_ports.lock);

  PROCEND open_specified_tcp_port;
?? OLDTITLE ??
?? NEWTITLE := 'open_specified_udp_port', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to open the specified UDP port
{ for the given IP address. If the specified port has already been
{ opened for the given IP address, an abnormal status is returned to
{ the caller. However, if the port has been opened for another IP address,
{ it is now opened for the given IP address also.

  PROCEDURE open_specified_udp_port
    (    ip_address: nat$sk_ip_address;
         port: nat$sk_port_number;
     VAR status: ost$status);

    VAR
      i: integer,
      new_open_port: ^nlt$udp_open_port,
      open_port: ^nlt$udp_open_port,
      previous_open_port: ^^nlt$udp_open_port;

    status.normal := TRUE;
    osp$set_job_signature_lock (nlv$udp_ports.lock);

{ Check if the specified port number is open over the given IP address.

    open_port := nlv$udp_ports.open_ports;
    previous_open_port := ^nlv$udp_ports.open_ports;

  /check_if_port_open/
    WHILE open_port <> NIL DO
      IF open_port^.port = port THEN
        IF open_port^.ip_address = ip_address THEN
          osp$set_status_abnormal (nac$status_id, nae$sk_port_already_in_use, 'UDP', status);
          osp$append_status_integer (osc$status_parameter_delimiter, port, 10, FALSE, status);
          EXIT /check_if_port_open/; {----->
        ELSEIF (open_port^.ip_address = nac$sk_all_ip_addresses) OR
              (ip_address = nac$sk_all_ip_addresses) THEN
          osp$set_status_abnormal (nac$status_id, nae$sk_port_already_in_use, 'UDP', status);
          osp$append_status_integer (osc$status_parameter_delimiter, port, 10, FALSE, status);
          EXIT /check_if_port_open/; {----->
        IFEND;
      IFEND;
      previous_open_port := ^open_port^.next_entry;
      open_port := open_port^.next_entry;
    WHILEND /check_if_port_open/;

    IF status.normal THEN

{ Specified port/IP_address has not been opened yet.

      REPEAT
        ALLOCATE new_open_port IN nav$network_paged_heap^;
        IF new_open_port = NIL THEN
          syp$cycle;
        IFEND;
      UNTIL new_open_port <> NIL;
      new_open_port^.next_entry := NIL;
      new_open_port^.port := port;
      new_open_port^.ip_address := ip_address;
      previous_open_port^ := new_open_port;
    IFEND;

    osp$clear_job_signature_lock (nlv$udp_ports.lock);

  PROCEND open_specified_udp_port;
?? OLDTITLE ??
?? NEWTITLE := 'open_tcp_port', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to assign the next available
{ port number and to open it for the specified IP address. The port
{ number assigned should not be open on any other IP address.

  PROCEDURE open_tcp_port
    (    ip_address: nat$sk_ip_address;
     VAR port: nat$sk_port_number;
     VAR status: ost$status);

    VAR
      assigned_port: nat$sk_port_number,
      i: integer,
      open_port: ^nlt$tcp_open_port,
      previous_open_port: ^^nlt$tcp_open_port;

    status.normal := TRUE;
    osp$set_job_signature_lock (nlv$tcp_ports.lock);
    port := 0;
    assigned_port := nlv$tcp_ports.next_assignable_port;

  /assign_port/
    REPEAT

{ Verify that the port is not already open for any IP address.

      previous_open_port := ^nlv$tcp_ports.open_ports;
      WHILE (previous_open_port^ <> NIL) AND (previous_open_port^^.port <> assigned_port) DO
        previous_open_port := ^previous_open_port^^.next_entry;
      WHILEND;
      IF previous_open_port^ = NIL THEN
        port := assigned_port;
        REPEAT
          ALLOCATE open_port IN nav$network_paged_heap^;
          IF open_port = NIL THEN
            syp$cycle;
          IFEND;
        UNTIL open_port <> NIL;
        open_port^.next_entry := NIL;
        open_port^.port := port;
        open_port^.count := 1;
        open_port^.listen_active := FALSE;
        open_port^.ip_address := ip_address;
        previous_open_port^ := open_port;
        IF port = nlc$sk_max_assigned_port THEN
          nlv$tcp_ports.next_assignable_port := nlc$sk_min_assigned_port;
        ELSE
          nlv$tcp_ports.next_assignable_port := port + 1;
        IFEND;
        EXIT /assign_port/; {----->
      ELSE { port is already open
        IF assigned_port = nlc$sk_max_assigned_port THEN
          assigned_port := nlc$sk_min_assigned_port;
        ELSE
          assigned_port := assigned_port + 1;
        IFEND;
      IFEND;
    UNTIL assigned_port = nlv$tcp_ports.next_assignable_port;

    IF port = 0 THEN
      osp$set_status_abnormal (nac$status_id, nae$sk_no_available_port, 'TCP', status);
    IFEND;

    osp$clear_job_signature_lock (nlv$tcp_ports.lock);

  PROCEND open_tcp_port;
?? OLDTITLE ??
?? NEWTITLE := 'open_udp_port', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to assign the next available
{ port number and to open it for the specified IP address. The port
{ number assigned should not be open on any other IP address.

  PROCEDURE open_udp_port
    (    ip_address: nat$sk_ip_address;
     VAR port: nat$sk_port_number;
     VAR status: ost$status);

    VAR
      assigned_port: nat$sk_port_number,
      open_port: ^nlt$udp_open_port,
      previous_open_port: ^^nlt$udp_open_port;

    status.normal := TRUE;
    osp$set_job_signature_lock (nlv$udp_ports.lock);
    port := 0;
    assigned_port := nlv$udp_ports.next_assignable_port;

  /assign_port/
    REPEAT

{ Verify that the port is not already open for any IP address.

      previous_open_port := ^nlv$udp_ports.open_ports;
      WHILE (previous_open_port^ <> NIL) AND (previous_open_port^^.port <> assigned_port) DO
        previous_open_port := ^previous_open_port^^.next_entry;
      WHILEND;
      IF previous_open_port^ = NIL THEN
        port := assigned_port;
        REPEAT
          ALLOCATE open_port IN nav$network_paged_heap^;
          IF open_port = NIL THEN
            syp$cycle;
          IFEND;
        UNTIL open_port <> NIL;
        open_port^.next_entry := NIL;
        open_port^.port := port;
        open_port^.ip_address := ip_address;
        previous_open_port^ := open_port;
        IF port = nlc$sk_max_assigned_port THEN
          nlv$udp_ports.next_assignable_port := nlc$sk_min_assigned_port;
        ELSE
          nlv$udp_ports.next_assignable_port := port + 1;
        IFEND;
        EXIT /assign_port/; {----->
      ELSE { port is already open
        IF assigned_port = nlc$sk_max_assigned_port THEN
          assigned_port := nlc$sk_min_assigned_port;
        ELSE
          assigned_port := assigned_port + 1;
        IFEND;
      IFEND;
    UNTIL assigned_port = nlv$udp_ports.next_assignable_port;

    IF port = 0 THEN
      osp$set_status_abnormal (nac$status_id, nae$sk_no_available_port, 'UDP', status);
    IFEND;

    osp$clear_job_signature_lock (nlv$udp_ports.lock);

  PROCEND open_udp_port;
?? OLDTITLE ??
?? NEWTITLE := 'verify_socket_options', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to verify the selected
{ socket options against the socket type and other attributes
{ of the socket.

  PROCEDURE verify_socket_options
    (    job_socket: ^nat$sk_job_socket;
         options: nat$sk_socket_options;
     VAR status: ost$status);

    VAR
      caller_id: ost$caller_identifier,
      i: integer,
      option_p: ^nat$sk_socket_option;

?? NEWTITLE := 'P$SET_INVALID_OPTION', EJECT ??

    PROCEDURE p$set_invalid_option
      (    socket_type: nat$sk_socket_type);

      VAR
        str: string (3);

      IF socket_type = nac$sk_tcp_socket THEN
        str := 'TCP';
      ELSE
        str := 'UDP';
      IFEND;

      osp$set_status_abnormal (nac$status_id, nae$sk_invalid_option, str, status);
      osp$append_status_integer (osc$status_parameter_delimiter, $INTEGER (option_p^.option_kind), 10, TRUE,
            status);
      EXIT verify_socket_options; {----->

    PROCEND p$set_invalid_option;
?? OLDTITLE ??
?? EJECT ??

    status.normal := TRUE;

    FOR i := 1 TO UPPERBOUND (options) DO
      option_p := ^options [i];
      CASE option_p^.option_kind OF
      = nac$sk_null_opt =
        ;
      = nac$sk_interface_mode_opt =

{ This option can be specified for either socket type.

      = nac$sk_interface_timeout_opt =

{ This options can be specified for either socket type.

      = nac$sk_checksum_opt =
        IF job_socket^.socket_type = nac$sk_tcp_socket THEN
          p$set_invalid_option (job_socket^.socket_type);
        IFEND;

      = nac$sk_traffic_pattern_opt =
        IF (option_p^.traffic_pattern = 0) OR ((option_p^.traffic_pattern >
              nac$sk_udp_last_traffic_pattern) AND (job_socket^.socket_type = nac$sk_udp_socket)) OR
              ((option_p^.traffic_pattern > nac$sk_tcp_last_traffic_pattern) AND
              (job_socket^.socket_type = nac$sk_tcp_socket)) THEN
          osp$set_status_condition (nae$sk_incorrect_option, status);
          osp$append_status_integer (osc$status_parameter_delimiter, $INTEGER (option_p^.option_kind), 10,
                TRUE, status);
          RETURN; {----->
        IFEND;

      = nac$sk_graceful_close_opt =
        IF job_socket^.socket_type = nac$sk_udp_socket THEN
          p$set_invalid_option (job_socket^.socket_type);
        IFEND;

      = nac$sk_selection_criteria_opt =
        IF job_socket^.socket_type = nac$sk_udp_socket THEN
          p$set_invalid_option (job_socket^.socket_type);
        ELSEIF job_socket^.tcp_socket_type = nlc$tcp_listen_socket THEN
          osp$set_status_condition (nae$sk_listen_already_active, status);
          osp$append_status_integer (osc$status_parameter_delimiter, job_socket^.identifier, 10, TRUE,
                status);
          RETURN; {----->
        IFEND;

      = nac$sk_local_addr_enabled_opt =
        IF job_socket^.socket_type = nac$sk_tcp_socket THEN
          p$set_invalid_option (job_socket^.socket_type);
        IFEND;

      = nac$sk_user_cache_enabled_opt =
        IF job_socket^.socket_type = nac$sk_tcp_socket THEN
          p$set_invalid_option (job_socket^.socket_type);
        IFEND;

      = nac$sk_reuse_address_opt =
        IF job_socket^.socket_type = nac$sk_udp_socket THEN
          p$set_invalid_option (job_socket^.socket_type);
        IFEND;

      = nac$sk_broadcast_enabled_opt =
        IF job_socket^.socket_type = nac$sk_tcp_socket THEN
          p$set_invalid_option (job_socket^.socket_type);
        IFEND;
        #CALLER_ID (caller_id);
        IF caller_id.ring > osc$sj_ring_3 THEN
          osp$set_status_abnormal (nac$status_id, nae$sk_invalid_user, 'BROADCAST', status);
          RETURN; {----->
        IFEND;

      ELSE { Invalid socket option
        p$set_invalid_option (job_socket^.socket_type);
      CASEND;
    FOREND;

  PROCEND verify_socket_options;
?? OLDTITLE ??
MODEND nam$sk_socket_layer;
