/*   Server comlink protocol file
     Copyright 1994-1999 Quanterra, Inc.
     Written by Woodrow H. Owens

Edit History:
   Ed Date      By  Changes
   -- --------- --- ---------------------------------------------------
    0 23 Mar 94 WHO First created.
    1  9 Apr 94 WHO Command Echo processing added.
    2 15 Apr 94 WHO Commands done.
    3 30 May 94 WHO If rev 1 or higher ultra record received, set comm_mask.
    4  9 Jun 94 WHO Consider a "busy" client still active if it is a
                    foreign client, without trying to do a Kill (0).
                    Cleanup to avoid warnings.
    5 11 Jun 94 WHO Martin-Marietta support added.
    6  9 Aug 94 WHO Set last_good and last_bad fields in linkstat.
    7 11 Aug 94 WHO Add support for network.
    8 25 Sep 94 WHO Allow receiving RECORD_HEADER_3.
    9  3 Nov 94 WHO Use SOLARIS2 definition to alias socket parameter.
   10 13 Dec 94 WHO Remove record size in seedheader function, COMSERV always
                    uses 512 byte blocks.
   11 16 Jun 95 WHO Don't try to open closed network connection until
                    netdly_cnt reaches netdly. Clear netto_cnt timeout
                    counter when a packet is received.
   12 20 Jun 95 WHO Updates due to link adj and link record packets. Transmitted
                    packet circular buffer added to accomodate grouping.
   13 15 Aug 95 WHO Set frame_count for Q512 and MM256 data packets.
   14  2 Oct 95 WHO Implement new link_pkt/ultra_req handshaking protocol.
   15 28 Jan 96 DSN Update check_input to handle unexpected characters better.
                    Correctly assign state when unexpected character is found.
   16  3 Jun 96 WHO Start of conversion to OS9
   17  4 Jun 96 WHO Comparison for dbuf.seq being positive removed, seq
                    is unsigned. cli_addr, network, and station made external.
   18  7 Jun 96 WHO Check result of "kill" with ERROR, not zero.
   19 13 Jun 96 WHO Adjust seedname and location fields for COMMENTS.
   20  3 Aug 96 WHO If anystation flag is on, don't check station, and show what
                    station came from. If noultra is on, don't poll for ultra
                    packets.
   21  7 Dec 96 WHO Add support for Blockettes and UDP.
   22 11 Jun 97 WHO Clear out remainder of packets that are received with
                    less than 512 bytes. Convert equivalent of header_flag
                    in blockette packet to seed sequence number.
   23 27 Jul 97 WHO Handle FLOOD_PKT.
   24 17 Oct 97 WHO Add VER_COMLINK
   25 23 Dec 98 WHO Use link_retry instead of a fixed 10 seconds for
                    polling for link packets (VSAT).
   26 22 Feb 99 PJM Modified comlink to create mcomlink.c. This allows
                    multicast reception by comlink.
   27 09 Aug 02 PAF cleaned up verbosity messages

    The changes from comlink.c to mcomlink.c were done to support reception
    of packets on a multicast interface by comserv. The packets are to be
    the 512byte SEED packets that are available to comserv clients. The
    conversion done by the standard comlink from qsl format to SEED is not
    done here. Various parts of the protocal done by comserv are removed,
    because in the multicast configuration, no acknowledgements are used.
    The def MSERV is used to comment out unused blocks. This should be
    defined near the start of this file.
    The intention what to minimize the number of modification to the original
    comlink.c to minimize the size effects of changes to the code.

*/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#ifndef _OSK
#include <termio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#else
#include <stdlib.h>
#include <sgstat.h>
#include <sg_codes.h>
#include "os9inet.h"
#endif
#include "quanstrc.h"
#include "stuff.h"
#include "service.h"
#include "cfgutil.h"
#include "server.h"
#include "timeutil.h"
#ifdef _OSK
#include "os9stuff.h"
#endif

/* Include routines which extract information from the seed header */

#include "mservutils.h"

/* Define MSERV to use in excluding code in this file */

#define MSERV 1



short VER_COMLINK = 27 ;

extern seed_net_type network ;
extern complong station ;
long reconfig_on_err = 25 ;
boolean anystation = FALSE ;

extern boolean noultra ;
extern pchar src, srcend, dest, destend, term ;
extern unsigned char sbuf[BLOB] ;
extern DA_to_DP_buffer dbuf ;
extern byte last_packet_received ;
extern byte lastchar ;
extern int path ;
extern int sockfd ;
extern struct sockaddr_in cli_addr ;
extern int upmemid ;
extern DP_to_DA_buffer temp_pkt ;
extern tcrc_table crctable ;
extern tclients clients[MAXCLIENTS] ;
extern pserver_struc base ;

extern byte inphase ;
extern byte upphase ;
extern long seq ;
extern long comm_mask ;
extern boolean verbose ;
extern boolean rambling ;
extern boolean insane ;
extern linkstat_rec linkstat ;
extern boolean detavail_ok ;
extern boolean detavail_loaded ;
extern boolean first ;
extern boolean firstpacket ;
extern boolean seq_valid ;
extern boolean xfer_down ;
extern boolean xfer_down_ok ;
extern boolean xfer_up_ok ;
extern boolean map_wait ;
extern boolean ultra_seg_empty ;
extern boolean override ;
extern boolean follow_up ;
extern boolean serial ;
extern boolean udplink ;
extern boolean notify ;
extern short ultra_percent ;
extern unsigned short lowest_seq ;
extern short linkpoll ;
extern long con_seq ;
extern long netdly_cnt ;
extern long netdly ;
extern long netto_cnt ;
extern long grpsize ;
extern long grptime ;
extern long link_retry ;
extern double last_sent ;
extern short combusy ;
extern byte cmd_seq ;
extern link_record curlink ;
extern string59 xfer_destination ;
extern string59 xfer_source ;
extern byte ultra_seg[14] ;
extern byte detavail_seg[14] ;
extern seg_map_type xfer_seg ;
extern unsigned short seg_size ;
extern unsigned short xfer_size ;
extern unsigned short xfer_up_curseg ;
extern unsigned short xfer_bytes ;
extern unsigned short xfer_total ;
extern unsigned short xfer_last ;
extern unsigned short xfer_segments ;
extern unsigned short cal_size ;
extern unsigned short used_size ;
extern unsigned short ultra_size ;
extern unsigned short detavail_size ;
extern cal_record *pcal ;
extern short sequence_mod ;
extern short vcovalue ; 
extern short xfer_resends ;
extern short mappoll ;
extern short sincemap ;
extern short down_count ;
extern short minctr ;
extern short txwin ;
extern download_struc *pdownload ;     
extern ultra_type *pultra ;
extern tupbuf *pupbuf ;
DP_to_DA_msg_type replybuf ;
extern DP_to_DA_msg_type gmsg ;
 
extern string3 seed_names[20][7] ;
extern location_type seed_locs[20][7] ;
 
static byte this_packet ;

static int maxbytes = 0 ;

long julian (time_array *gt) ;
pchar time_string (double jul) ;
tring_elem *getbuffer (short qnum) ;
boolean bufavail (short qnum) ;
boolean checkmask (short qnum) ;
double seedheader (header_type *h, seed_fixed_data_record_header *sh) ;
double seedblocks (seed_record_header *sl, commo_record *log) ;
signed char encode_rate (short rate) ;
void seedsequence (seed_record_header *seed, long seq) ;

static short seq_seen[DEFAULT_WINDOW_SIZE] = { 0,0,0,0,0,0,0,0};

typedef struct sockaddr *psockaddr ;

typedef struct
  begin
    byte cmd ;              /* command number */
    byte ack ;              /* acknowledgement of packet n */
    byte dpseq ;            /* sequence for DP to DA commands */
    char leadin ;           /* leadin character */
    short len ;             /* Length of message */
    DP_to_DA_msg_type buf ; /* buffer for the raw message */
  end txbuf_type ;
  
static short nextin = 0 ;
static short nextout = 0 ;
static txbuf_type txbuf[64] ;

pchar seednamestring (seed_name_type *sd, location_type *loc) ;

  void gcrcinit (void)
    begin
      short count, bits ;
      long tdata, accum ;

      for (count = 0 ; count < 256 ; count++)
        begin
          tdata = ((long) count) << 24 ;
          accum = 0 ;
          for (bits = 1 ; bits <= 8 ; bits++)
            begin
              if ((tdata eor accum) < 0)
                then
                  accum = (accum << 1) eor CRC_POLYNOMIAL ;
                else
                  accum = (accum << 1) ;
              tdata = tdata << 1 ;
            end
          crctable[count] = accum ;
        end
    end

  long gcrccalc (pchar b, short len)
    begin
      complong crc ;

      crc.l = 0 ;
      while (len-- > 0)
        crc.l = (crc.l << 8) eor crctable[(crc.b[0] eor *b++) and 255] ;
      return crc.l ;
    end

  unsigned short checksum (pchar addr, short size)
    begin
      unsigned short ck ;

      ck = 0 ;
      while (size-- > 0)
          ck = ck + ((short) *addr++ and 255) ;
      return ck ;
    end

  void send_window (void)
    begin
      short i, len ;
      int numwrit ;
      pchar ta, tstr ;
      char transmit_buf[2 * sizeof(DP_to_DA_buffer) + 2] ;
      complong ltemp ;
      byte b ;
      txbuf_type *cur ;

      return;
/* To prevent mserv from sending information, this routine is stubbed out */

#ifndef MSERV

      while (txwin > 0)
        begin
          cur = &txbuf[nextout] ;
          temp_pkt.c.cmd = cur->cmd ;
          temp_pkt.c.ack = cur->ack ;
          temp_pkt.msg = cur->buf ;
          temp_pkt.msg.scs.dp_seq = cur->dpseq ;
          transmit_buf[0] = cur->leadin ;
          len = cur->len ;
          ta = (pchar) ((long) &temp_pkt + 6) ;
          temp_pkt.c.chksum = checksum(ta, len - 6) ;
          ltemp.l = gcrccalc(ta, len - 6) ;
          temp_pkt.c.crc = ltemp.s[0] ;
          temp_pkt.c.crc_low = ltemp.s[1] ;
          if (udplink)
            then
              numwrit = sendto (path, (pchar) &temp_pkt, len, 0,
                        (psockaddr) &cli_addr, sizeof(cli_addr)) ;
            else
              begin
                tstr = &transmit_buf[1] ;
                ta = (pchar) &temp_pkt ;
                for (i = 0 ; i < len ; i++)
                  begin
                    b = (*ta >> 4) and 15 ;
                    if (b > 9)
                      then
                        *tstr++ = b + 55 ;
                      else
                        *tstr++ = b + 48 ;
                    b = *ta++ and 15 ;
                    if (b > 9)
                      then
                        *tstr++ = b + 55 ;
                      else
                        *tstr++ = b + 48 ;
                  end
                end
          txwin-- ;
          if (insane)
            then
              if (cur->cmd == ACK_MSG)
                then
                  printf ("Acking packet %d from slot %d, packets queued=%d\n", cur->ack, nextout, txwin) ;
                else
                  printf ("Sending command %d from slot %d, packets queued=%d\n", ord(cur->cmd), nextout, txwin) ;
           nextout = ++nextout and 63 ;
           if ((path >= 0) land lnot udplink)
            then
              begin
                numwrit = write(path, (pchar) &transmit_buf, len * 2 + 1) ;
                if ((numwrit < 0) land (verbose))
                  then
                    perror ("Error writing to port ") ;
              end
          last_sent = dtime () ;
        end

#endif

    end

  void send_tx_packet (byte nr, byte cmd_var, DP_to_DA_msg_type *msg)
    begin
      txbuf_type *cur ;

/* To prevent the mserv from sending acks, or link establishment requests, */
/* this routine with TX's is stubbed out. */

      return; 

#ifndef MSERV

      cur = &txbuf[nextin] ;
      if (cmd_var == ACK_MSG)
        then
          begin
            cur->cmd = cmd_var ;
            cur->ack = nr ;
            cur->leadin = LEADIN_ACKNAK ;
            cur->len = DP_TO_DA_LENGTH_ACKNAK ;
            cur->dpseq = 0 ;
          end
        else
          begin
            cur->cmd = cmd_var ;
            cur->ack = last_packet_received ;
            cur->buf = *msg ;
            cur->dpseq = cmd_seq++ ;
            cur->len = DP_TO_DA_LENGTH_CMD ;
            cur->leadin = LEADIN_CMD ;
          end ;
      txwin++ ;
      if (insane)
        then
          printf ("Placing outgoing packet in slot %d, total in window=%d\n", nextin, txwin) ;
      nextin = ++nextin and 63 ;
      if (txwin >= grpsize)
        then
          send_window () ;

#endif

    end
    
  void send_ack (void)
    begin
      DP_to_DA_msg_type msg ;

      return;
/* To prevent mserv from sending acks, this routine is stubbed out . */

#ifndef MSERV

      last_packet_received = this_packet ;
      send_tx_packet (this_packet, ACK_MSG, &msg) ;

#endif

    end

  void request_ultra (void)
    begin
      DP_to_DA_msg_type mmsg ;

/* To prevent mserv from sending requests, this routine is stubbed out */

      return;

#ifndef MSERV

      if (path < 0)
        then
          return ;
      mmsg.scs.cmd_type = ULTRA_REQ ;
      send_tx_packet (0, ULTRA_REQ, &mmsg) ;
      if (rambling)
        then
          printf ("Requesting ultra packet\n") ;

#endif

    end

  void request_link (void)
    begin
      DP_to_DA_msg_type mmsg ;

/* To prevent mserv from sending requests, this routine is stubbed out */

      return;

#ifndef MSERV

      linkpoll = 0 ;
      if (path < 0)
        then
          return ;
      mmsg.scs.cmd_type = LINK_REQ ;
      send_tx_packet (0, LINK_REQ, &mmsg) ;
      if (rambling)
        then
          printf ("Requesting link packet\n") ;

#endif

    end

  void request_map (void)
    begin
      DP_to_DA_msg_type mmsg ;

/* To prevent mserv from requesting maps, this routine is stubbed out. */

      return;

#ifndef MSERV

      mmsg.us.cmd_type = UPLOAD ;
      mmsg.us.dp_seq = cmd_seq ;
      mmsg.us.return_map = TRUE ;
      if (upphase == WAIT_CREATE_OK)
        then
          begin
            mmsg.us.upload_control = CREATE_UPLOAD ;
            mmsg.us.up_union.up_create.file_size = xfer_size ;
            memcpy (mmsg.us.up_union.up_create.file_name, xfer_destination, 60) ;
          end
        else
          mmsg.us.upload_control = MAP_ONLY ;
      send_tx_packet (0, UPLOAD, &mmsg) ;
      mappoll = 30 ;

#endif

    end

  void reconfigure (boolean full)
    begin
      short i ;

/* To prevent mserv from reconfiguring a link, this routine is stubbed out. */


#ifndef MSERV

      if (full)
        then
          begin
            linkstat.linkrecv = FALSE ;
            seq_valid = FALSE ;
            lowest_seq = 300 ;
            con_seq = 0 ;
            linkpoll = 0 ;
            request_link () ;
          end
       if (linkstat.ultraon)
        then
          begin
            linkstat.ultrarecv = FALSE ;
            ultra_seg_empty = TRUE ;
            for (i = 0 ; i < 14 ; i++)
              ultra_seg[i] = 0 ;
            if (ultra_size)
              then
                begin
                  free(pultra) ;
                  ultra_size = 0 ;
                end
            ultra_percent = 0 ;
            if (lnot full)
              then
                request_ultra () ;
          end
        else
          for (i = 0 ; i < DEFAULT_WINDOW_SIZE ; i++)
            seq_seen[i] = 0 ;

#endif

    end

  void clearmsg (DP_to_DA_msg_type *m)
    begin
      m->mmc.dp_seq = 0 ;
      m->mmc.cmd_parms.param0 = 0 ;
      m->mmc.cmd_parms.param1 = 0 ;
      m->mmc.cmd_parms.param2 = 0 ;
      m->mmc.cmd_parms.param3 = 0 ;
      m->mmc.cmd_parms.param4 = 0 ;
      m->mmc.cmd_parms.param5 = 0 ;
      m->mmc.cmd_parms.param6 = 0 ;
      m->mmc.cmd_parms.param7 = 0 ;
    end
    
  boolean checkbusy (void) /* returns TRUE if client is foreign or alive */
    begin
      return (combusy != NOCLIENT) land 
         ((clients[combusy].client_address->client_uid != base->server_uid) lor
#ifdef _OSK
          (kill(clients[combusy].client_pid, SIGWAKE) != ERROR)) ;
#else
          (kill(clients[combusy].client_pid, 0) != ERROR)) ;
#endif
    end

  void do_abort (void)
    begin
      DP_to_DA_msg_type mmsg ;
      comstat_rec *pcom ;
      download_result *pdr ;

      if (xfer_up_ok)
        then
          begin
            xfer_up_ok = FALSE ;
            clearmsg (&mmsg) ;
            mmsg.us.cmd_type = UPLOAD ;
            mmsg.us.dp_seq = cmd_seq ;
            mmsg.us.return_map = FALSE ;
            mmsg.us.upload_control = ABORT_UPLOAD ;
            send_tx_packet (0, UPLOAD, &mmsg) ;
            clearmsg (&mmsg) ;
            mmsg.us.cmd_type = UPLOAD ;
            mmsg.us.dp_seq = cmd_seq ;
            mmsg.us.return_map = FALSE ;
            mmsg.us.upload_control = ABORT_UPLOAD ;
            send_tx_packet (0, UPLOAD, &mmsg) ;
            shmdt((pchar)pupbuf) ;
            if (checkbusy ())
              then
                begin
                  pcom = (pvoid) clients[combusy].outbuf ;
                  if (upmemid != NOCLIENT)
                    then
                      begin
                        shmctl(upmemid, IPC_RMID, NULL) ;
                        upmemid = NOCLIENT ;
                      end
                  if (pcom->completion_status == CSCS_INPROGRESS)
                    then
                      pcom->completion_status = CSCS_ABORTED ;
                  combusy = NOCLIENT ;
                end
            xfer_size = 0 ;
          end ;
      if (xfer_down_ok)
        then
          begin
            xfer_down_ok = FALSE ;
            clearmsg (&mmsg) ;
            mmsg.scs.cmd_type = DOWN_ABT ;
            send_tx_packet (0, DOWN_ABT, &mmsg) ;
            clearmsg (&mmsg) ;
            mmsg.scs.cmd_type = DOWN_ABT ;
            send_tx_packet (0, DOWN_ABT, &mmsg) ;
            if (pdownload != NULL)
              then
                shmdt((pchar)pdownload) ;
            if (checkbusy ())
              then
                begin
                  pcom = (pvoid) clients[combusy].outbuf ;
                  pdr = (pvoid) &pcom->moreinfo ;
                  if (pdr->dpshmid != NOCLIENT)
                    then
                      begin
                        shmctl(pdr->dpshmid, IPC_RMID, NULL) ;
                        pdr->dpshmid = NOCLIENT ;
                      end
                  if (pcom->completion_status == CSCS_INPROGRESS)
                    then
                      pcom->completion_status = CSCS_ABORTED ;
                  combusy = NOCLIENT ;
                end
          end
    end

  void next_segment (void)
    begin
      DP_to_DA_msg_type mmsg ;
      unsigned short i ;
      unsigned short h ;
      pchar p1, p2 ;
      boolean allsent ;
      unsigned short off, cnt ; 
      comstat_rec *pcom ;
      upload_result *pupres ;

      clearmsg (&mmsg) ;
      mmsg.scs.cmd_type = UPLOAD ;
      mmsg.us.return_map = FALSE ;
      mmsg.us.upload_control = SEND_UPLOAD ;
      if (checkbusy ())
        then
          begin
            pcom = (pvoid) clients[combusy].outbuf ;
            pupres = (pvoid) &pcom->moreinfo ;
          end
        else
          begin
            do_abort () ;
            return ;
          end
      if (upphase == SENDING_UPLOAD)
        then
          begin
            i = xfer_up_curseg ;
            allsent = TRUE ;
            while (i < xfer_segments)
              begin
                if (((xfer_seg[i div 8]) and ((byte) (1 << (i mod 8)))) == 0)
                  then
                    begin
                      allsent = FALSE ;
                      off = seg_size * i ;
                      mmsg.us.up_union.up_send.byte_offset = off ;
                      xfer_up_curseg = i + 1 ;
                      mmsg.us.up_union.up_send.seg_num = xfer_up_curseg ;
                      if (((unsigned int) off + (unsigned int) seg_size) >= (unsigned int) xfer_size)
                        then
                          begin
                            cnt = xfer_size - off ;
                            xfer_up_curseg = 0 ;
                            mmsg.us.return_map = TRUE ;
                            mappoll = 30 ;
                            sincemap = 0 ;
                            upphase = WAIT_MAP ;
                            if (xfer_resends < 0)
                              then
                                xfer_resends = 0 ;
                          end
                        else
                          begin
                            cnt = seg_size ;
                            if (++sincemap >= 10)
                              then
                                begin
                                  mmsg.us.return_map = TRUE ;
                                  mappoll = 30 ;
                                  sincemap = 0 ;
                                  upphase = WAIT_MAP ;
                                end
                          end ;
                      mmsg.us.up_union.up_send.byte_count = cnt ;
                      p1 = (pchar) ((long) pupbuf + off) ;
                      p2 = (pchar) &mmsg.us.up_union.up_send.bytes ;
                      memcpy(p2, p1, cnt) ;
                      xfer_bytes = xfer_bytes + cnt ;
                      if (xfer_bytes > xfer_total)
                        then
                          xfer_bytes = xfer_total ;
                      if (xfer_resends >= 0)
                        then
                          xfer_resends++ ;
                      pupres->bytecount = xfer_bytes ;
                      pupres->retries = xfer_resends ;
                      send_tx_packet (0, UPLOAD, &mmsg) ;
                      break ;
                    end
                i++ ;
              end
            if (allsent)
              then
                for (i = 0 ; i < xfer_segments ; i++)
                  if (((xfer_seg[i div 8]) and ((byte)(1 << (i mod 8)))) == 0)
                    then
                      begin
                        xfer_up_curseg = 0 ;
                        upphase = WAIT_MAP ;
                        request_map () ;
                        return ;
                      end
            if (allsent)
              then
                begin
                  xfer_up_ok = FALSE ;
                  upphase = UP_IDLE ;
                  mmsg.us.upload_control = UPLOAD_DONE ;
                  xfer_size = 0 ;
                  shmdt((pchar)pupbuf) ;
                  pcom->completion_status = CSCS_FINISHED ;
                  combusy = NOCLIENT ;
                  send_tx_packet (0, UPLOAD, &mmsg) ;
                end
          end
    end

  void process_upmap (void)
    begin
      comstat_rec *pcom ;
      
      mappoll = 0 ;
      switch (upphase)
        begin
          case WAIT_CREATE_OK :
            begin
              if (dbuf.data_buf.cu.upload_ok)
                then
                  begin
                    upphase = SENDING_UPLOAD ;
                    memcpy ((pchar) &xfer_seg, (pchar) &dbuf.data_buf.cu.segmap, sizeof(seg_map_type)) ;
                  end
                else
                  begin
                    upphase = UP_IDLE ;
                    xfer_up_ok = FALSE ;
                    shmdt((pchar)pupbuf) ;
                    if (checkbusy ())
                      then
                        begin
                          pcom = (pvoid) clients[combusy].outbuf ;
                          if (upmemid != NOCLIENT)
                            then
                              begin
                                shmctl(upmemid, IPC_RMID, NULL) ;
                                upmemid = NOCLIENT ;
                              end
                          if (pcom->completion_status == CSCS_INPROGRESS)
                            then
                              pcom->completion_status = CSCS_CANT ;
                          combusy = NOCLIENT ;
                        end
                    xfer_size = 0 ;
                  end
              break ;
            end
          case WAIT_MAP :
            begin
              upphase = SENDING_UPLOAD ;
              memcpy ((pchar) &xfer_seg, (pchar) &dbuf.data_buf.cu.segmap, sizeof(seg_map_type)) ;
            end
        end
    end

  void setseed (byte cmp, byte str, seed_name_type *seed, location_type *loc)
    begin
      memcpy((pchar) seed, (pchar) &seed_names[cmp][str], 3) ;
      memcpy((pchar) loc, (pchar) &seed_locs[cmp][str], 2) ;
    end
    
  double seed_jul (seed_time_struc *st)
    begin
      double t ;
      
      t = jconv (st->yr - 1900, st->jday) ;
      t = t + (double) st->hr * 3600.0 + (double) st->minute * 60.0 + 
          (double) st->seconds + (double) st->tenth_millisec * 0.0001 ;
      return t ;
    end

  void process (void)
    begin
      typedef char string5[6] ;
      typedef block_record *tpbr ;
      
      char rcelu[2] = {'N', 'Y'} ;
      string5 lf[2] = {"QSL", "Q512"} ;
      short da_sum, dp_sum, i, j, l ;
      long da_crc, dp_crc ;
      boolean b, full ;
      byte k ;
      pchar ta ;
      seed_name_type sn ;
      seed_net_type sn2 ;
      header_type workheader ;
      short size ;
      unsigned short bc, xfer_offset ;
      typedef error_control_packet *tecp ;
      tecp pecp ;
      pchar p1, p2 ;
      time_quality_descriptor *ptqd ;
      calibration_result *pcal ;
      clock_exception *pce ;
      squeezed_event_detection_report *ppick ;
      commo_reply *preply ;
      complong ltemp ;
      tring_elem *freebuf ;
      seed_fixed_data_record_header *pseed ;
      comstat_rec *pcr, *pcom ;
      download_result *pdr ;
      det_request_rec *pdetavail ;
      download_struc *pds ;
      tpbr pbr ;
      long *pl ;
      char seedst[5] ;
      char s1[64], s2[64] ;
      char myseqno[7];

/* Create a temporary storage area for the received packets seed header */

      seed_fixed_data_record_header my_seed_header;

/* Classify the packets on an integer packet_type */
  
      int packet_type;

/* Determine a first sample_time from the seed packet */

      double packet_time;
      double recv_time;
      int res;


      linkstat.total_packets++ ;
      size = (short) ((long) term - (long) dest);

/* At this point, all known packets are 512 bytes in length, so reject */
/* any that are not this size. However, packets such as opaques blockettes */
/* have not been received and tested and this may reject packets which we */
/* want, but are not the expected size. Log an error to show that it is */
/* happening, so that we can make modifications to this if needed. */

      if (size != 512) 
        then
          begin
            fprintf(stderr, "Unknown packet size %d\n",size);
            return;
          end

/* In the mserv version, all 512 packets that we get are considered valid */
/* Here we process the packet, and put it into the next avaiable comserv */
/* Ring buffer slot. */

      else 
        then 
          begin

            netto_cnt = 0 ; /* got a packet, reset timeout */
            linkstat.last_good = dtime () ;

/* Update the station name in the seed header if required. */
/* We won't worry about any station name embedded in blockettes or contents. */
	    if (station.l != 0) {
		char *s = long_str (station.l);
		char *d = dest+8;
		memcpy (d, "     ", 5);
		for (i=0; i<5; i++) {
		    if (*s) *(d++) = *(s++);
		}
	    }

/* Copy the header portion of the received packet, and then decode it */
/* into packet types if possible. */

            memcpy(&my_seed_header,dest,64);

/* Extract information that is in the qsl header, but not in the SEED */
/* header so the comserv can use the information to label packets */

	    packet_type = classify_packet(&my_seed_header);
            packet_time = dtime();

/* Here is some planned logic to update the ring buffer element with */
/* a correct time of data sample. However I could not find a use for */
/* this time stamp, and the conversion from seed to time_array was */
/* non trivial, so I'll time stamp the packets with time of reception */
/* I though dataspy used this header time of sample, but when I look */
/* I see it decodes the seed header itself. So until I find a need */
/* for an actual data time stamp in this header, I'll use recpetion time. */

#ifndef MSERV

            res =         header_to_double_time(&my_seed_header,
						&packet_time);
            if(res != TRUE)
            { 
	      if(verbose)
              {	
                fprintf(stderr,"Error converting SEED time.\n");
              }
              packet_time = dtime();
            }
#endif

/* The following commented out stuff is information on packets received. */
/* It was in the original comserv, and available if you turned */
/* verbosity up. Since the data structures changed, I need to reimplement */
/* this and I have not yet done it. */
	      if(rambling)
		{
			recv_time = dtime();
		    	strncpy(myseqno,dest,6);
		    	memset(&myseqno[6],0,1);
     			printf("Mcast Packet: %.2s %.4s %.3s Seq=%s received at %s\n", 
					my_seed_header.header.seednet,
					my_seed_header.header.station_ID_call_letters,
					my_seed_header.header.channel_id,
					myseqno,
					time_string(recv_time));
		}

#ifndef MSERV
                    if (0)
                      then
                        begin
                          if (anystation)
                            then

     printf("%4.4s.", &my_seed_header.station) ;
     printf("%s Header time %s", 
	seednamestring(&dbuf.data_buf.cr.h.seedname,
       &dbuf.data_buf.cr.h.location), 
	time_string(freebuf->user_data.header_time)) ;

     printf(", received at %s\n", 
       time_string(freebuf->user_data.reception_time)) ;
                        end

#endif

            switch(packet_type)
            {
              case RECORD_HEADER_1 :
              {

                freebuf = getbuffer (DATAQ) ;    /* get free buffer */
	        if(freebuf == NULL)
                {
                  fprintf(stderr,"Station blocked.\n");
	          fprintf(stderr,"No blocking clients.\n");
	          exit(12);
                }
                break;
              }
              case BLOCKETTE :
              {

                freebuf = getbuffer (BLKQ) ;    /* get free buffer */
	        if(freebuf == NULL)
                {
                  fprintf(stderr,"Station blocked.\n");
	          fprintf(stderr,"No blocking clients.\n");
	          exit(12);
                }
                break;
              }
              case COMMENTS :
              {
                freebuf = getbuffer (MSGQ) ;    /* get free buffer */
	        if(freebuf == NULL)
                {
                  fprintf(stderr,"Station blocked.\n");
	          fprintf(stderr,"No blocking clients.\n");
	          exit(12);
                }
                break;
              }
              case CLOCK_CORRECTION :
              {
                freebuf = getbuffer (TIMQ) ;    /* get free buffer */
	        if(freebuf == NULL)
                {
                  fprintf(stderr,"Station blocked.\n");
	          fprintf(stderr,"No blocking clients.\n");
	          exit(12);
                }
                break;
              }
              case DETECTION_RESULT :
              {
                freebuf = getbuffer (DETQ) ;    /* get free buffer */
	        if(freebuf == NULL)
                {
                  fprintf(stderr,"Station blocked.\n");
	          fprintf(stderr,"No blocking clients.\n");
	          exit(12);
                }
                break;
              }
              case END_OF_DETECTION :
              {
                freebuf = getbuffer (DATAQ) ;    /* get free buffer */
	        if(freebuf == NULL)
                {
                  fprintf(stderr,"Station blocked.\n");
	          fprintf(stderr,"No blocking clients.\n");
	          exit(12);
                }
                break;
              }
              case CALIBRATION :
              {
                freebuf = getbuffer (CALQ) ;    /* get free buffer */
	        if(freebuf == NULL)
                {
                  fprintf(stderr,"Station blocked.\n");
	          fprintf(stderr,"No blocking clients.\n");
	          exit(12);
                }
                break;
              }
              default :
              {
                fprintf(stderr,"Unknown Packet Type %d\n",packet_type);
                return;
              }
            } /* End of Switch */


/* Now freebuf points to the proper comserv ring buffer. Now we */
/* put the data into the buffer. */


             pseed = (pvoid) &freebuf->user_data.data_bytes ;

             freebuf->user_data.reception_time = dtime () ;    
				            /* reception time */

             freebuf->user_data.header_time = packet_time;

             memcpy (pseed,dest,512) ;
    
             return;

/* All the other packet handling logic here is to sort packets that have a */
/* QSL header. Mserv does not have the header, so this logic is stubbed out. */

#ifndef MSERV

            switch (dbuf.data_buf.cr.h.frame_type)
              begin
                case RECORD_HEADER_1 : ;
                case RECORD_HEADER_2 : ;
                case RECORD_HEADER_3 :
                  begin
                    if (linkstat.data_format == CSF_Q512)
                      then
                        dbuf.data_buf.cr.h.frame_count = 7 ;
                    if (checkmask (DATAQ))
                      then
                        return ;
                      else
                        send_ack () ;
                    if (lnot linkstat.ultraon)
                      then
                        begin
                          setseed(dbuf.data_buf.cr.h.component, dbuf.data_buf.cr.h.stream,
                                  &dbuf.data_buf.cr.h.seedname, &dbuf.data_buf.cr.h.location) ;
                          memcpy((pchar) &dbuf.data_buf.cr.h.seednet, (pchar) &network, 2) ;
                        end
                    if (override)
                      then
                        memcpy((pchar) &dbuf.data_buf.cr.h.station, (pchar) &station, 4) ;
                    else if ((lnot anystation) land (memcmp((pchar) &dbuf.data_buf.cr.h.station, (pchar) &station, 4) != 0))
                      then
                        printf ("Station %4.4s data received instead of %4.4s\n", 
                           &dbuf.data_buf.cr.h.station, &station) ;
                    if (firstpacket)
                      then
                        begin
                          firstpacket = FALSE ;
                          if (linkstat.ultraon)
                            then
                              begin
                                if (lnot linkstat.linkrecv)
                                  then
                                    request_link () ;
                              end
                        end
              /* Unless an option is specified to override the station, an error should
                 be generated if the station does not agree. Same goes for network.
              */
              /* put into data buffer ring */
                    freebuf = getbuffer (DATAQ) ;                  /* get free buffer */
                    pseed = (pvoid) &freebuf->user_data.data_bytes ;
                    freebuf->user_data.reception_time = dtime () ;    /* reception time */
                    freebuf->user_data.header_time = seedheader (&dbuf.data_buf.cr.h, pseed) ; /* convert header to SEED */
                    if (rambling)
                      then
                        begin
                          if (anystation)
                            then
                              printf("%4.4s.", &dbuf.data_buf.cr.h.station) ;
                          printf("%s Header time %s", seednamestring(&dbuf.data_buf.cr.h.seedname,
                             &dbuf.data_buf.cr.h.location), time_string(freebuf->user_data.header_time)) ;
                          printf(", received at %s\n", 
                              time_string(freebuf->user_data.reception_time)) ;
                        end
                    p1 = (pvoid) ((long) pseed + 64) ;             /* skip header */
                    memcpy (p1, (pchar) &dbuf.data_buf.cr.frames, 448) ;   /* and copy data portion */
                    break ;
                  end
                case BLOCKETTE :
                  begin
                    if (checkmask (BLKQ))
                      then
                        return ;
                      else
                        send_ack () ;
                    pbr = (tpbr) &dbuf.data_buf ;
                    strcpy ((pchar)&seedst, "     ") ; /* initialize to spaces */
                    j = 0 ;
                    for (i = 0 ; i <= 3 ; i++)
                      if (station.b[i] != ' ')
                        then
                          seedst[j++] = station.b[i] ; /* move in non space characters */
                    if (override)
                      then
                        memcpy((pchar) &(pbr->hdr.station_ID_call_letters), (pchar) &seedst, 5) ;
                    else if ((lnot anystation) land
                         (memcmp((pchar) &(pbr->hdr.station_ID_call_letters), (pchar) &seedst, 5) != 0))
                      then
                        printf ("Station %4.4s data received instead of %4.4s\n", 
                           &(pbr->hdr.station_ID_call_letters), &station) ;
              /* Unless an option is specified to override the station, an error should
                 be generated if the station does not agree. Same goes for network.
              */
              /* put into data buffer ring */
                    freebuf = getbuffer (BLKQ) ;                  /* get free buffer */
                    pseed = (pvoid) &freebuf->user_data.data_bytes ;
                    freebuf->user_data.reception_time = dtime () ;    /* reception time */
                    freebuf->user_data.header_time = seed_jul (&pbr->hdr.starting_time) ; /* convert SEED time to julian */
                    if (rambling)
                      then
                        begin
                          if (anystation)
                            then
                              printf("%4.4s.", (pchar) &(pbr->hdr.station_ID_call_letters)) ;
                          printf("%s Header time %s", seednamestring(&(pbr->hdr.channel_id),
                             &(pbr->hdr.location_id)), time_string(freebuf->user_data.header_time)) ;
                          printf(", received at %s\n", 
                              time_string(freebuf->user_data.reception_time)) ;
                        end
                    pl = (pvoid) &pbr->hdr ;
                    seedsequence (&pbr->hdr, *pl) ;
                    memcpy ((pchar) &freebuf->user_data.data_bytes, (pchar) pbr, 512) ; /* copy record into buffer */
                    break ;
                  end
                 case EMPTY : ;
                 case FLOOD_PKT :
                  if (checkmask (-1))
                    then
                      return ;
                    else
                      begin
                        send_ack () ;
                        linkstat.sync_packets++ ;
                        break ;
                      end
                case COMMENTS :
                  begin
                    if (checkmask (MSGQ))
                      then
                        return ;
                      else
                        send_ack () ;
                    if (linkstat.ultraon)
                      then
                        begin
                          p1 = (pchar) &dbuf.data_buf.cc.ct ;
                          p1 = (pchar) ((long) p1 + (unsigned long) *p1 + 1) ;
                          p2 = (pchar) &dbuf.data_buf.cc.cc_station ;
                          memcpy(p2, p1, sizeof(long) + sizeof(seed_net_type) +
                                    sizeof(location_type) + sizeof(seed_name_type)) ;
                        end
                    if (lnot linkstat.ultraon)
                      then
                        begin
                          memcpy((pchar) &dbuf.data_buf.cc.cc_station, (pchar) &station, 4) ;
                          memcpy((pchar) &dbuf.data_buf.cc.cc_net, (pchar) &network, 2) ;
                        end
                    if (override)
                      then
                        memcpy((pchar) &dbuf.data_buf.cc.cc_station, (pchar) &station, 4) ;
                    else if ((lnot anystation) land (memcmp((pchar) &dbuf.data_buf.cc.cc_station, (pchar) &station, 4) != 0))
                      then
                        printf ("Station %4.4s message received instead of %4.4s\n", 
                           &dbuf.data_buf.cc.cc_station, &station) ;
                    if (linkstat.ultraon land (lnot linkstat.ultrarecv) land
                       (strncasecmp((pchar) &dbuf.data_buf.cc.ct, "FROM AQSAMPLE: Acquisition begun", 32) == 0))
                      then
                        request_ultra () ;
                  /* Put into blockette ring */
                    if (rambling)
                      then
                        begin
                          dbuf.data_buf.cc.ct[dbuf.data_buf.cc.ct[0]+1] = '\0' ;
                          if (anystation)
                            then
                              printf("%4.4s.", &dbuf.data_buf.cc.cc_station) ;
                          printf ("%s\n", &dbuf.data_buf.cc.ct[1]) ;
                        end ;
                    freebuf = getbuffer (MSGQ) ;
                    pseed = (pvoid) &freebuf->user_data.data_bytes ;
                    freebuf->user_data.reception_time = dtime () ;    /* reception time */
                    freebuf->user_data.header_time = seedblocks ((pvoid) pseed, &dbuf.data_buf) ;
                    break ;
                  end
                case CLOCK_CORRECTION :
                  begin
                    if (checkmask (TIMQ))
                      then
                        return ;
                      else
                        send_ack () ;
                    if (vcovalue < 0)
                      then
                        if (linkstat.ultraon)
                          then
                            vcovalue = dbuf.data_buf.ce.header_elog.clk_exc.vco ;
                          else
                            begin
                              ptqd = (pvoid) &dbuf.data_buf.ce.header_elog.clk_exc.correction_quality ;
                              vcovalue = (unsigned short) ptqd->time_base_VCO_correction * 16 ;
                            end
                    pce = &dbuf.data_buf.ce.header_elog.clk_exc ;
                    if (lnot linkstat.ultraon)
                      then
                        begin
                          memcpy((pchar) &pce->cl_station, (pchar) &station, 4) ;
                          memcpy((pchar) &pce->cl_net, (pchar) &network, 2) ;
                        end
                    if (override)
                      then
                        memcpy((pchar) &pce->cl_station, (pchar) &station, 4) ;
                    else if ((lnot anystation) land (memcmp((pchar) &pce->cl_station, (pchar) &station, 4) != 0))
                      then
                        printf ("Station %4.4s timing received instead of %4.4s\n", 
                           &pce->cl_station, &station) ;
                   /* put into blockette ring */
                    freebuf = getbuffer (TIMQ) ;
                    pseed = (pvoid) &freebuf->user_data.data_bytes ;
                    freebuf->user_data.reception_time = dtime () ;    /* reception time */
                    freebuf->user_data.header_time = seedblocks ((pvoid) pseed, &dbuf.data_buf) ;
                    if (rambling)
                      then
                        begin
                         if (anystation)
                          then
                            printf("%4.4s.", &pce->cl_station) ;
                         printf("Clock time-mark %s",
                              time_string(freebuf->user_data.header_time)) ;
                          printf(", received at %s\n", 
                              time_string(freebuf->user_data.reception_time)) ;
                        end
                    break ;
                  end
                case DETECTION_RESULT :
                  begin
                    if (checkmask (DETQ))
                      then
                        return ;
                      else
                        send_ack () ;
                    ppick = &dbuf.data_buf.ce.header_elog.det_res.pick ;
                    if (lnot linkstat.ultraon)
                      then
                        begin
                          setseed (ppick->component, ppick->stream,
                                   &ppick->seedname, &ppick->location) ;
                          memset((pchar) &ppick->detname, ' ', 24) ;
                          ppick->sedr_sp1 = 0 ;
                          memcpy((pchar) &ppick->ev_station, (pchar) &station, 4) ;
                          memcpy((pchar) &ppick->ev_network, (pchar) &network, 2) ;
                        end
                    if (override)
                      then
                        memcpy((pchar) &ppick->ev_station, (pchar) &station, 4) ;
                    else if ((lnot anystation) land (memcmp((pchar) &ppick->ev_station, (pchar) &station, 4) != 0))
                      then
                        printf ("Station %4.4s detection received instead of %4.4s\n", 
                           &ppick->ev_station, &station) ;
                    /* put into blockette ring */
                    freebuf = getbuffer (DETQ) ;
                    pseed = (pvoid) &freebuf->user_data.data_bytes ;
                    freebuf->user_data.reception_time = dtime () ;
                    freebuf->user_data.header_time = seedblocks ((pvoid) pseed, &dbuf.data_buf) ;
                    if (rambling)
                      then
                        begin
                          if (anystation)
                            then
                              printf("%4.4s.", &ppick->ev_station) ;
                          printf("%3.3s Detection   %s",
                              &dbuf.data_buf.ce.header_elog.det_res.pick.seedname,
                              time_string(freebuf->user_data.header_time)) ;
                          printf(", received at %s\n", 
                              time_string(freebuf->user_data.reception_time)) ;
                        end
                    break ;
                  end
                case END_OF_DETECTION :
                  begin
                    if (checkmask (DATAQ))
                      then
                        return ;
                      else
                        send_ack () ;
                    freebuf = getbuffer (DATAQ) ;                  /* get free buffer */
                    pseed = (pvoid) &freebuf->user_data.data_bytes ;
                    ppick = &dbuf.data_buf.ce.header_elog.det_res.pick ;
                    if (lnot linkstat.ultraon)
                      then
                        begin
                          setseed (ppick->component, ppick->stream,
                                   &ppick->seedname, &ppick->location) ;
                          ppick->sedr_sp1 = 0 ;
                          memcpy((pchar) &ppick->ev_station, (pchar) &station, 4) ;
                          memcpy((pchar) &ppick->ev_network, (pchar) &network, 2) ;
                        end
                    if (override)
                      then
                        memcpy((pchar) &ppick->ev_station, (pchar) &station, 4) ;
                    else if ((lnot anystation) land (memcmp((pchar) &ppick->ev_station, (pchar) &station, 4) != 0))
                      then
                        printf ("Station %4.4s end of detection received instead of %4.4s\n", 
                           &ppick->ev_station, &station) ;
                    freebuf->user_data.reception_time = dtime () ;    /* reception time */
                    freebuf->user_data.header_time = seedblocks ((pvoid) pseed, &dbuf.data_buf) ;
                    if (rambling)
                      then
                        begin
                          if (anystation)
                            then
                              printf("%4.4s.", &ppick->ev_station) ;
                          printf("%3s Detect End  %s",
                              &dbuf.data_buf.ce.header_elog.det_res.pick.seedname,
                              time_string(freebuf->user_data.header_time)) ;
                          printf(", received at %s\n", 
                              time_string(freebuf->user_data.reception_time)) ;
                        end
                    break ;
                  end
                case CALIBRATION :
                  begin
                    if (checkmask (CALQ))
                      then
                        return ;
                      else
                        send_ack () ;
                    pcal = &dbuf.data_buf.ce.header_elog.cal_res ;
                    if (lnot linkstat.ultraon)
                      then
                        begin
                          setseed (pcal->cr_component, pcal->cr_stream,
                                   &pcal->cr_seedname, &pcal->cr_location) ;
                          setseed (pcal->cr_input_comp, pcal->cr_input_strm,
                                   &pcal->cr_input_seedname, &pcal->cr_input_location) ;
                          pcal->cr_flags2 = 0 ;
                          pcal->cr_0dB = 0 ;
                          pcal->cr_0dB_low = 0 ;
                          pcal->cr_sfrq = Hz1_0000 ;
                          pcal->cr_filt = 0 ;
                          memcpy ((pchar) &pcal->cr_station, (pchar) &station, 4) ;
                          memcpy ((pchar) &pcal->cr_network, (pchar) &network, 2) ;
                        end
                    if (override)
                      then
                        memcpy((pchar) &pcal->cr_station, (pchar) &station, 4) ;
                    else if ((lnot anystation) land (memcmp((pchar) &pcal->cr_station, (pchar) &station, 4) != 0))
                      then
                        printf ("Station %4.4s calibration received instead of %4.4s\n", 
                           &pcal->cr_station, &station) ;
                    /* store in blockette ring */
                    freebuf = getbuffer (CALQ) ;
                    pseed = (pvoid) &freebuf->user_data.data_bytes ;
                    freebuf->user_data.reception_time = dtime () ;    /* reception time */
                    freebuf->user_data.header_time = seedblocks ((pvoid) pseed, &dbuf.data_buf) ;
                    if (rambling)
                      then
                        begin
                          if (anystation)
                            then
                              printf("%4.4s.", &pcal->cr_station) ;
                          printf("%3.3s Calibration %s",
                              &dbuf.data_buf.ce.header_elog.cal_res.cr_seedname, 
                              time_string(freebuf->user_data.header_time)) ;
                          printf(", received at %s\n", 
                              time_string(freebuf->user_data.reception_time)) ;
                        end
                    break ;
                  end
                case ULTRA_PKT :
                  begin
                    if (checkmask (-1))
                      then
                        return ;
                      else
                        send_ack () ;
                    if (lnot linkstat.ultrarecv)
                      then
                        begin
                          preply = &dbuf.data_buf.cy ;
                          full = TRUE ;
                          if (ultra_seg_empty)
                            then
                              begin
                                ultra_seg_empty = FALSE ;
                                pultra = (pvoid) malloc(preply->total_bytes) ;
                              end
                          ultra_seg[preply->this_seg div 8] = ultra_seg[preply->this_seg div 8] or
                                          (byte) (1 << (preply->this_seg mod 8))  ;
                          ta = (pchar) ((long) pultra + preply->byte_offset) ;
                          memcpy(ta, (pchar) &preply->bytes, preply->byte_count) ;
                          j = 0 ;
                          for (i = 1 ; i <= preply->total_seg ; i++)
                            if ((ultra_seg[i div 8] and ((byte) (1 << (i mod 8)))) == 0)
                              then
                                full = FALSE ;
                              else
                                j++ ;
                          ultra_percent = (float) ((j / preply->total_seg) * 100.0) ;
                          if (full)
                            then
                              begin
                                linkstat.ultrarecv = TRUE ;
                                vcovalue = pultra->vcovalue ;
                                if (pultra->ultra_rev >= 1)
                                  then
                                    comm_mask = pultra->comm_mask ;
                                if (rambling)
                                  then
                                    printf("Ultra record received with %d bytes\n",
                                           preply->total_bytes) ;
                              end
                        end
                    break ;
                  end
                case DET_AVAIL :
                  begin
                    if (checkmask (-1))
                      then
                        return ;
                      else
                        send_ack () ;
                    if (detavail_ok land (lnot detavail_loaded))
                      then
                        begin
                          preply = &dbuf.data_buf.cy ;
                          full = TRUE ;
                          pdetavail = NULL ;
                          /* Make sure there is a valid client for this data */
                          if (checkbusy ())
                            then
                              begin
                                pcr = (pvoid) clients[combusy].outbuf ;
                                if ((unsigned int) clients[combusy].outsize >= (unsigned int) preply->total_bytes)
                                  then
                                    pdetavail = (pvoid) &pcr->moreinfo ;
                              end
                          if (pdetavail == NULL)
                            then
                              begin
                                detavail_ok = FALSE ;
                                combusy = NOCLIENT ;
                                break ;
                              end
                          detavail_seg[preply->this_seg div 8] = detavail_seg[preply->this_seg div 8] or
                                          (byte) (1 << (preply->this_seg mod 8))  ;
                          ta = (pchar) ((long) pdetavail + preply->byte_offset) ;
                          memcpy (ta, (pchar) &preply->bytes, preply->byte_count) ;
                          j = 0 ;
                          for (i = 1 ; i <= preply->total_seg ; i++)
                            if ((detavail_seg[i div 8] and ((byte) (1 << (i mod 8)))) == 0)
                              then
                                full = FALSE ;
                              else
                                j++ ;
                          if (full)
                            then
                              begin
                                detavail_loaded = TRUE ;
                                pcr->completion_status = CSCS_FINISHED ;
                                combusy = NOCLIENT ;
                              end
                        end
                    break ;
                  end
                case DOWNLOAD :
                  begin
                    if (checkmask (-1))
                      then
                        return ;
                      else
                        send_ack () ;
                    if (xfer_down_ok land checkbusy ())
                      then
                        begin
                          preply = &dbuf.data_buf.cy ;
                          pcom = (pvoid) clients[combusy].outbuf ;
                          pdr = (pvoid) &pcom->moreinfo ;
                          if (preply->this_seg == 1)
                            then
                              begin
                                pds = (pvoid) &preply->bytes ;
                                xfer_total = pds->file_size ;
                                pdr->fsize = xfer_total ;
                                strpcopy (s1, pds->file_name) ;
                                strpcopy (s2, xfer_source) ;
                                if (strcasecmp((pchar) &s1, (pchar) &s2) != 0)
                                  then
                                    begin
                                      do_abort () ;
                                      break ;
                                   end
                                if (lnot pds->filefound)
                                  then
                                    begin
                                      pcom->completion_status = CSCS_NOTFOUND ;
                                      do_abort () ;
                                      break ;
                                    end
                                if (pds->toobig)
                                  then
                                    begin
                                      pcom->completion_status = CSCS_TOOBIG ;
                                      break ;
                                    end
                              end
                          if (xfer_size == 0)
                            then
                              begin
                                xfer_size = preply->total_bytes ;
                                pdr->dpshmid = shmget(IPC_PRIVATE, xfer_size, IPC_CREAT or PERM) ;
                                if (pdr->dpshmid == ERROR)
                                  then
                                    begin
                                      pcom->completion_status = CSCR_PRIVATE ;
                                      do_abort () ;
                                      break ;
                                    end
                                pdownload = (pvoid) shmat (pdr->dpshmid, NULL, 0) ;
                                if ((int) pdownload == ERROR)
                                  then
                                    begin
                                      pcom->completion_status = CSCR_PRIVATE ;
                                      do_abort () ;
                                      break ;
                                    end
                                xfer_segments = preply->total_seg ;
                              end
/* Isolate client from header, start the data module with the actual file contents */
                          xfer_offset = sizeof(download_struc) - 65000 ; /* source bytes to skip */
                          ta = (pchar) ((long) pdownload + preply->byte_offset - xfer_offset) ; /* destination */
                          p1 = (pchar) &preply->bytes ; /* source */
                          bc = preply->byte_count ; /* number of bytes */
                          if (preply->this_seg == 1)
                            then
                              begin
                                bc = bc - xfer_offset ; /* first record contains header */
                                p1 = p1 + xfer_offset ;
                                ta = ta + xfer_offset ;
                              end
                          memcpy (ta, p1, bc) ;
                          i = preply->this_seg - 1 ;
                          j = i div 8 ;
                          k = (byte) (1 << (i mod 8)) ;
                          if ((xfer_seg[j] and k) == 0)
                            then
                              pdr->byte_count = pdr->byte_count + bc ;  /* not already received */
                          xfer_seg[j] = xfer_seg[j] or k ;
                          l = 0 ;
                          for (i = 0 ; i <= 127 ; i++)
                            begin
                              k = xfer_seg[i] ;
                              for (j = 0 ; j <= 7 ; j++)
                                if ((k and (byte) (1 << j)) != 0)
                                  then
                                    l++ ;
                            end
                          if ((unsigned int) l >= (unsigned int) xfer_segments)
                            then
                              begin
                                xfer_down_ok = FALSE ;
                                down_count = 0 ;
                                pdr->byte_count = xfer_total ;
                                shmdt((pchar)pdownload) ;
                                pcom->completion_status = CSCS_FINISHED ;
                                combusy = NOCLIENT ;
                                xfer_size = 0 ;
                              end
                        end
                    else if (++down_count > 5)
                      then
                        begin
                          xfer_down_ok = TRUE ;
                          do_abort () ;
                        end
                    break ;
                  end
                case UPMAP :
                  begin
                    if (checkmask (-1))
                      then
                        return ;
                      else
                        send_ack () ;
                    if (xfer_up_ok)
                      then
                        process_upmap () ;
                    break ;
                  end
                case CMD_ECHO : 
                  begin
                    if (checkmask (-1))
                      then
                        return ;
                      else
                        send_ack () ;
                    if (checkbusy ())
                      then
                        begin
                          pcom = (pvoid) clients[combusy].outbuf ;
                          preply = &dbuf.data_buf.cy ;
                          memcpy ((pchar) &replybuf, (pchar) &preply->bytes, preply->byte_count) ;
                          if ((replybuf.ces.dp_seq == pcom->command_tag) land
                              (pcom->completion_status == CSCS_INPROGRESS))
                            then
                              if (follow_up)
                                then
                                  begin /* Set AUTO DAC, now send prepared ACCURATE DAC */
                                    if (++cmd_seq == 0)
                                      then
                                        cmd_seq = 1 ;
                                    gmsg.mmc.dp_seq = cmd_seq ;
                                    send_tx_packet (0, gmsg.mmc.cmd_type, &gmsg) ;
                                    pcom->command_tag = cmd_seq ;
                                    follow_up = FALSE ;
                                  end
                                else
                                  begin
                                    pcom->completion_status = CSCS_FINISHED ;
                                    combusy = NOCLIENT ;
                                  end
                        end 
                    break ;
                  end
                default :
                  begin
                    linkstat.last_bad = dtime () ;
                    printf ("INVALID RECORD TYPE=%d\n", dbuf.data_buf.cr.h.frame_type) ;
                    if (checkmask (-1))
                      then
                        return ;
                      else
                        send_ack () ;
                    break ;
                  end
              end

#endif

          end


/* This else part is a remant of checking the packet for a checksum error. */
/* In the mserv version, all packets are accepted, and the UDP unreliable */
/* but valid if delivered principle is used as packet validation. */

#ifndef MSERV

        else
          begin
            linkstat.last_bad = dtime () ;
            linkstat.check_errors++ ;
            if (verbose)
              then
                printf ("CHECKSUM ERROR ON PACKET %d, BYTE COUNT=%d, CHECKSUM ERRORS=%d\n",
                        last_packet_received, size, linkstat.check_errors) ;
          end
#endif


    end

  void fillbuf (void)
    begin
      int numread, clilen ;
#ifdef _OSK
      u_int32 err, count ;
#endif

      src = (pchar) &sbuf ;
      srcend = src ;
      if (lnot serial)
        then
          begin
            if ((path < 0) land (netdly_cnt >= netdly))
              then
                begin
                  clilen = sizeof(cli_addr) ;
                  path = accept(sockfd, (psockaddr) &cli_addr, &clilen) ;
                  netdly_cnt = 0 ;
                  if ((verbose) land (path >= 0))
                    then
                      printf ("Network connection with DA opened\n") ;
                  if (linkstat.ultraon)
                    then
                      begin
                        linkstat.linkrecv = FALSE ;
                        linkstat.ultrarecv = FALSE ;
                        seq_valid = FALSE ;
                        linkpoll = link_retry ;
                      end
                end
            if (path < 0)
              then
                return ;
          end
#ifdef _OSK
      if (serial)
        then /* make sure we don't block here */
          begin
            err = _os_gs_ready(path, &count) ;
            if ((err == EOS_NOTRDY) lor (count == 0))
              then
                return ;
            else if (err != 0)
              then
                begin
                  numread = -1 ;
                  errno = err ;
                end
              else
                begin
                  if (count > BLOB)
                    then
                      count = BLOB ;
                  err = blockread (path, count, src) ;
                  if (err == 208)
                    then
                      numread = read(path, src, count) ;
                  else if (err == 0)
                    then
                      numread = count ;
                    else
                      begin
                        numread = -1 ;
                        errno = err ;
                      end
                end
          end
        else
          numread = read(path, src, BLOB) ;  
#else
      numread = read(path, src, BLOB) ;
#endif
      if (numread > 0)
       then
         begin
           srcend = (pchar) ((long) srcend + numread) ;
           if ((insane) /* land (numread > maxbytes) */)
             then
               begin
                 maxbytes = numread ;
                 printf ("%d bytes read\n", numread) ;
               end
         end
      else if (numread < 0)
        then
          if (errno != EWOULDBLOCK)
            then
              begin
                linkstat.io_errors++ ;
                linkstat.lastio_error = errno ;
                if (serial)
                  then
                    begin
                      if (verbose)
                        then
                          perror ("Error reading from port ") ;
                    end
                  else
                    begin
                      if (verbose)
                        then
                          perror ("Network connection with DA closed\n") ;
                      shutdown(path, 2) ;
                      close(path) ;
                      seq_valid = FALSE ;
                      if (linkstat.ultraon)
                        then
                          begin
                            linkstat.linkrecv = FALSE ;
                            linkstat.ultrarecv = FALSE ;
                          end
                       path = -1 ; /* signal not open */
                    end
              end
    end

  short inserial (pchar b)
    begin
      int numread ;

      if (src == srcend)
        then
          fillbuf () ;

      if (src != srcend)
        then
          begin
            *b = *src++ ;
            return 1 ;
          end
        else
          return 0 ;
    end

  void dlestrip (void)
    begin
      boolean indle ;

      term = NULL ;
      indle = (lastchar == DLE) ;
      while ((src != srcend) land (dest != destend))
        begin
          if (indle)
            then
              begin
                *dest++ = *src ;
                indle = FALSE ;
              end
          else if (*src == DLE)
            then
              indle = TRUE ;
          else if (*src == ETX)
            then
              begin
                term = dest ;
                src++ ;
                lastchar = NUL ;
                return ;
              end
            else
              *dest++ = *src ;
          src++ ;
        end
      if (indle)
        then
          lastchar = DLE ;
        else
          lastchar = NUL ;
    end

  void check_input (void)
    begin
      int numread ;
      short err ;
      char b ;

/* Define a SEED sequence Number var for debugging purposes. */


/* It should always be udplink in mserv */

      if (udplink)
      {

/* Use of recvfrom, versus recv. I went with recv because I don't */
/* need to know who sent the data to me. Recvfrom allows me to identify */
/* the ip address of the sender. I debated the size of the receive buffer. */
/* It is possible, use of a smaller recieve buffer preserves global system */
/* resources such as mbufs, but I can't confirm this, so I'll use a */
/* buffer with some extra room, which is the actual size of the sbuf var. */

/* In addition this is a nonblocking read. When we return we first check */
/* for a non-blocking return error code. This is the most common return. */
/* Next we check for a "good" read of 512 bytes. Then we will process */
/* this as a good message */


	    numread = recv(path,dest,512,0);

            if (numread < 0)
            {

/* If the errno is EWOULDBLOCK, then we want to return without any error.*/
/* This is the standard path out of this routine. */

	      if (errno != EWOULDBLOCK)
              {
                fprintf(stderr,"Errno on socket recv.\n");
                perror("socket error :\n");
              }
            }
            else if(numread == 512)
            {

/* This is a good read of a SEED packet*/

/* In a debug situation, this will print out the sequence number of */
/* each packet recieved. They are in order for a single channel */
/* but if you are recieving multiple channels, these numbers won't be */
/* in order */
                term = (pchar) ((long) dest + numread) ;
                process () ;
             }
             else
             {
                  fprintf(stderr,"Uknown packet size %d\n",numread);
             }
      }
      else /* This is the else to if (udplink). Should never happen */
      {

/* On the exception condition (that is, it should never happen) that */
/* mserv is not in udplink mode, report an error and exit. This will */
/* for the operator to find out what's causing mserv to exit. */

           fprintf(stderr,"Mserv not is udplink mode.\n");
           exit(12);
       }

/* This should be the common return from all parts of the if statement. */

    return;

/* The serial code should never be executed, so it is stubbed out. */

#ifndef MSERV

      if (src == srcend)
        then
          fillbuf () ;
      switch (inphase)
        begin
          case SOHWAIT :
            dest = (pchar) &dbuf.seq ;
          case SYNWAIT :
            begin
              while (inphase != INBLOCK)
                begin
                  lastchar = NUL ;
                  err = inserial(&b) ;
                  if (err != 1)
                    then
                      return ;
                  if ((b == SOH))
                    then
                      inphase = SYNWAIT ;
                  else if ((b == SYN) land (inphase == SYNWAIT))
                    then
                      inphase = INBLOCK ;
                    else 
                      inphase = SOHWAIT ;
                end
            end
          case INBLOCK :
            begin
              if (src == srcend)
                then
                  fillbuf ;
              if (src != srcend)
                then
                  begin
                    dlestrip () ;
                    if (dest == destend)
                      then
                        inphase = ETXWAIT ;
                    else if (term != NULL)
                      then
                        begin
                          inphase = SOHWAIT ;
                          process () ;
                          break ;
                        end
                      else
                        break ;
                  end
                else
                  break ;
            end
          case ETXWAIT :
            begin
              if (src == srcend)
                then
                  fillbuf ;
              err = inserial (&b) ;
              if (err == 1)
                then
                  if (b == ETX)
                    then
                      begin
                        inphase = SOHWAIT ;
                        term = dest ;
                        process () ;
                        break ;
                      end
                    else
                      begin
                        inphase = SOHWAIT ;
                        break ;
                      end
            end
        end

#endif

    end

