/*
  Socket service routines for the Remote Midas InterFace (RMIF).
  Currently coded for UDP and RDP packet protocols

  Feb 2005 KCS: Added timeData to sync up with quickly recycling remotes
  Oct 2006 DKC: Removed INTEL compiler warnings
  Oct 2009 JC4/NTN: Added addr_to_string(..) for BUG 1879 to print remote
                      IP address:port in verbose and log messages.
                    Fixed formAddr(..), funcName(..), and openSocket(..) to be
                      64-bit safe for BUG 1914.
  Oct 2011 NTN: Fix ICC 12 and GCC 4.4.x compiler error for undefined u_char
                  on RHEL6 by using "unsigned char" instead for BUG 2404.

  @author Jeff Schoen
  @author Ken Smith
  @author Dave Campbell (Mr. Classic)
  @author J. Czechowski IV & Neon Ngo
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <stdio.h> /* for printf() */
#include <unistd.h>
#include "cdefs.h"

#include "fcinterface.h" /* for fstring_len_t typedef under xm445 or later */
/* --- BEGIN --- for fstring_len_t typedef under xm444 and earlier and xm450-xm454 */
#include "xmversion.h"
#if XM_SYS_VERSION_AT_LEAST(4,5,5)
  /* fstring_len_t is defined in X-Midas 4.5.5+ */
#else
# if XM_SYS_VERSION_AT_LEAST(4,4,5) && !XM_SYS_VERSION_AT_LEAST(4,5,0)
  /* 2011-11-18: fstring_len_t is defined in X-Midas 4.4.5+ but not under 4.5.0 to 4.5.4 */
# else
  /* fstring_len_t is defined in X-Midas 4.4.5+ or 4.5.5 and greater */
  /* otherwise we have to define typedef for fstring_len_t for XM 4.4.4 and earlier */
#  if defined(_PGF77) || defined(_OSF)
     typedef int fstring_len_t;
#  else
     typedef size_t fstring_len_t;
#  endif
# endif
#endif
/* --- END --- for fstring_len_t typedef under xm444 and earlier and xm450-xm454 */

#if _VMS
#define openSocket opensocket
#define closeSocket closesocket
#define sendPacket sendpacket
#define recvPacket recvpacket
#define checkRemotes checkremotes
#define formAddr formaddr
#define setTime settime
#define setRemotes setremotes
#define sendMessage sendmessage
#define funcName funcname
#else
#define openSocket opensocket_
#define closeSocket closesocket_
#define sendPacket sendpacket_
#define recvPacket recvpacket_
#define checkRemotes checkremotes_
#define formAddr formaddr_
#define setTime settime_
#define setRemotes setremotes_
#define initialize initialize_
#define uncompress uncompress_
#define compress compress_
#define sendMessage sendmessage_
#define funcName funcname_
#endif

#define _DEBUG 0

struct sockaddr_in serveraddr;
struct sockaddr_in clientaddr;

#define BUFSIZE 32768
#define MAXMEMBER 32
#define MAXBUF 65536
#define MAXLAT   2

#define SET	 101
#define GET	 102
#define RET	 103
#define ACK	 104
#define OPEN	 105
#define CLOSE	 106
#define SEND	 107
#define RECV	 108
#define PING	 109
#define PONG	 110
#define DHDR	 111
#define DBUF	 112
#define ADDR	 113
#define MODIFY	 114
#define OPENED	 115
#define CLOSED	 116
#define DBUFC	 117
#define DBUFD	 118
#define GETPROP	 121
#define RETPROP	 122
#define RECEIPT  123
#define LINKTEST 124

#define FUNC 0
#define FLAG 1
#define INFO 2
#define REP  3
#define SEQ  4
#define TRY  5
#define RPT  6
#define ADJ  7
#define DATA 8

#define MINFUNCTION 101
#define MAXFUNCTION 124

char* functionList[] = {
  "SET", "GET", "RET", "ACK", "OPEN", "CLOSE",
  "SEND", "RECV", "PING", "PONG", "DHDR", "DBUF",
  "ADDR", "MODIFY", "OPENED", "CLOSED", "DBUFC", "DBUFD",
  "<UNSPECIFIED>", "<UNSPECIFIED>",
  "GETPROP", "RETPROP", "RECEIPT", "LINKTEST"
};
char *unknown = "Unknown";

char *functionName (int func) {
  if (func<MINFUNCTION || func>MAXFUNCTION) return unknown;
  else return functionList[func-MINFUNCTION];
}

void funcName (int func, char *buf, fstring_len_t lbuf) {
  char *s; int i,ls;
  if (func<MINFUNCTION || func>MAXFUNCTION) s = unknown;
  else s = functionList[func-MINFUNCTION];
  ls = strlen(s);
  for (i=0; i<ls; i++) buf[i]=s[i];
  for (i=ls; i<lbuf; i++) buf[i]=' ';
}


typedef struct {
  char  func,flag,info,rep,seq,try,rpt,adj;
  char  data[BUFSIZE];
} packetStruct;

typedef struct {
  char  name[40];
  int   addr[4];
  char  machine[4];
  char  state;
  char	rep;
  char  origin;
  char  maxRetry,maxWindow;
  char  fill[7];
  double timePong,timeRetry,latency,timeData;
  int   sendOff, sendSeq, sendChk, sendQueue, sendDrop, sendOther;
  int	sendOffset[128];
  int	sendLength[128];
  double sendTime[128];
  char	sendBuffer[MAXBUF];
  int   recvOff, recvSeq, recvChk, recvQueue, recvLost, recvReOrder;
  int	recvOffset[128];
  int	recvLength[128];
  char	recvBuffer[MAXBUF];
  double recvTime[128];
} remoteStruct;

typedef struct {
  char	dec,count;
  char	comp,flag;
  int_4	plotwidth;
  int_4	trim1,trim2;
} propMembStruct;

typedef struct {
  int_4	type,members;
  char  member[MAXMEMBER];
  char  channel[MAXMEMBER];
  char  name[31],rtype;
  int_4	phcb,len;
  char	value[40];
  propMembStruct pms[MAXMEMBER];
} propertyStruct;

#define DC_RAW 91
#define DC_BAND 92
#define DC_CONST 93
#define DC_PACK 94

#define CM_GLOBAL 0x01
#define CM_EXACT  0x02
#define CM_TRIM   0x04
#define CM_EDGE   0x08
#define CM_BAND1  0x10
#define CM_BAND2  0x20
#define CM_PKPACK 0x40
#define CM_LRPACK 0x80

#define RDP 0
#define UDP 1
#define TCP 2

static remoteStruct *remote;
static char lutl[256], lutu[256], lutd[256];
static char lutm[16] = { 0,1,2,3,4,5,6,8,10,12,15,18,21,24,28,32 };
static char lutn[8] = { 0,1,2,4,6,9,12,16 };
static double gtime=0;
static double timeLost=10;
static int nremote=0;
static int r0addrs[128*4];

static char recvseq[128];
static int recvaddrs[128*4];
static int rdpin=0;
static int myport=0;

static char testLinkBuf[128];
static int testLinkPkt=0;

/* Forward declarations */
int sendPacket(int *, packetStruct *,int *,int *,int *);
int testLink(int,int,int);
int sendLinkProblemReport(int *,char *,int,int,packetStruct *,int);
/* Declaration for routine in rmif.for */
void sendMessage(char *,int *,int *,packetStruct *,int *);

int initialize (remoteStruct *r) {
  int i,j,k;
  remote = r;
  /* fill in compression lookup tables */
  for (i=j=k=0; i<256; i++) {
    if (j<15 && lutm[j+1]-i < i-lutm[j]) j++;
    lutl[i]=j; lutu[i]=(j<<4);
    if (k<7 && lutd[k+1]-i < i-lutd[k]) k++;
    lutd[i]=k;
  }
  return 0;
}

void setTime (double *newtime) { gtime = *newtime; }
void setRemotes (int *n) { nremote = *n; }

int openSocket (int *port, int *prange, const char *ip, fstring_len_t lip) {

  int sock,status,toggle=1;
  socklen_t len;
  int nport = *port, lastport = *prange + *port;
  struct sockaddr_in address;
  struct hostent *hostent;
  unsigned int retbufsize, bufsize=65536;
  char ip_cstr[lip+1]; /* +1 to have space for \0 char in C string */
  int lip_cstr;        /* length of ip_cstr */

  sock = socket (AF_INET, SOCK_DGRAM, 0);
  if (sock<0) { perror("Err opening socket\n"); return -1; }

  strf2c(ip,lip, ip_cstr,lip+1, F2C_TRIM); /* convert Fortran string to C string */
  lip_cstr = strlen(ip_cstr); /* get length of ip_cstr */

  TRY_NEXT_PORT:
  *port = myport = nport;

  if ((ip == NULL) || (lip_cstr < 1)) {
    printf("rmif.c: Use 'default' IP address\n");
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(nport);
    serveraddr.sin_addr.s_addr = INADDR_ANY;
  } else {
    printf("rmif.c: Use '%s' IP address\n", ip_cstr);
    hostent = gethostbyname(ip_cstr);
    if (hostent == NULL) {
      printf("gethostbyname returned null\n");
      return -1;
    }
    serveraddr.sin_family = hostent->h_addrtype;
    serveraddr.sin_port = htons(nport);
    memcpy(&serveraddr.sin_addr, hostent->h_addr, hostent->h_length);
  }

  status = bind (sock, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
  if (status<0 && ++nport<lastport) goto TRY_NEXT_PORT;
  if (status<0) { perror("Err binding socket\n"); return -4; }

  status = ioctl (sock, FIONBIO, &toggle);
  if (status<0) { perror("Err unblocking socket\n"); return -2; }

  /* set socket size to the larger of 64K and the system default */
  len=4; status = getsockopt (sock, SOL_SOCKET, SO_RCVBUF, &retbufsize, &len);
  if (status==0 && retbufsize>bufsize) bufsize=retbufsize;

  len = sizeof(unsigned int);
  status = setsockopt (sock, SOL_SOCKET, SO_RCVBUF, &bufsize, len);
  if (status<0) { perror("Err sizing receive socket buffer\n"); return -3; }

  status = setsockopt (sock, SOL_SOCKET, SO_SNDBUF, &bufsize, len);
  if (status<0) { perror("Err sizing send socket buffer\n"); return -3; }

  len=4; status = getsockopt (sock, SOL_SOCKET, SO_RCVBUF, &retbufsize, &len);
  if (retbufsize<bufsize) printf ("RCV buffer is only %d bytes\n",retbufsize);

  len=4; status = getsockopt (sock, SOL_SOCKET, SO_SNDBUF, &retbufsize, &len);
  if (retbufsize<bufsize) printf ("SND buffer is only %d bytes\n",retbufsize);

  return sock;
}

void closeSocket (int *sock) {
  close (*sock);
}

int sendPack (int sock, int func, int info, char *data, int len, int ir, int flags) {
  packetStruct pack;
  pack.func=func; pack.flag=0; pack.info=info; pack.rep=0;
  pack.seq=0; pack.try=0; pack.rpt=0; pack.adj=0;
  memcpy ( (void*)pack.data, (void*)data, len);
  return sendPacket (&sock, &pack, &len, &ir, &flags);
}

int sendPacket (int *sock, packetStruct *pack, int *length, int *ir, int *flags) {
  int status, seq, off, addrlen=16, len=(*length+8);
  remoteStruct *r = remote+(*ir);
  /* dont send RDP if ir=0 since this is an unOpened remote */
  if (*flags == RDP && *ir > 0) {
    pack->seq = seq = (r->sendSeq++) & 0x7f;
    off = r->sendOff; if (off+len > MAXBUF) off=0;
    r->sendOffset[seq] = off;
    r->sendLength[seq] = len;
    memcpy ( (void*)(r->sendBuffer+off), (void*)pack, len);
    r->sendOff = off+len;
#if DEBUG
    printf("SockStore seq=%d off=%d bytes=%d\n",seq,r->sendOff,len);
#endif
    if (*ir==0) memcpy ( (void*)(r0addrs+seq*4), (void*)r->addr, 16);
    if (r->sendSeq-r->sendChk > r->maxWindow) { r->sendTime[seq] = -1; r->sendQueue++; return 0; }
    r->sendTime[seq]=gtime;
  }
  else pack->seq = -1;
  pack->try = 0;
  pack->rpt = 0;
#if DEBUG
  if (pack->seq>=0) printf("Sent seq=%d func=%d ir=%d len=%d\n",
	pack->seq,pack->func,*ir,len);
#endif
  status = sendto (*sock, pack, len, 0, (struct sockaddr *)r->addr, addrlen);
  if (status<=0) { printf("SockSendStatus = %d of %d\n",status,len); perror(""); }
  return status;
}

int recvPacket (int *sock, packetStruct *pack, int *length, int *ir, int *flags) {
  int status, rstatus, addrlen=16, off, len, i, j, seq, rdpmin;
  socklen_t addrlen_t;
  double latency; char f,s; remoteStruct *r;
  TRYAGAIN:
  *ir=0; r=remote;len = *length+8;
  memset( (void *)r->addr, 0, addrlen);
  addrlen_t = (socklen_t)addrlen;
  status = recvfrom (*sock, pack, len, 0, (struct sockaddr *)r->addr, &addrlen_t);
  if (status<8) return -1;
  /* index the remote this is from */
  for (i=1; i<=nremote; i++) {
    if (remote[0].addr[0]==remote[i].addr[0] &&
        remote[0].addr[1]==remote[i].addr[1] )
	{ *ir=i; r=remote+i; break; }
  }
  f=pack->func;
  s=pack->seq;
  seq=s;
  len = status;
#if DEBUG
  if (pack->seq>=0) printf("Got packet func=%d seq=%d try=%d info=%d\n",
		pack->func,pack->seq,pack->try,pack->info);
#endif
  if (f==RECEIPT) {
#if DEBUG
    printf("Got receipt for seq=%d\n",pack->seq);
#endif
    latency = gtime - r->sendTime[seq];
    if (r->sendTime[seq]<=0);  /* previously receipted */
    else if (latency>MAXLAT); /* outlyer */
    else if (pack->try>0); /* dont use specs on retry */
    else if (r->latency==0) r->latency = latency;
    else r->latency = (r->latency*.9 + latency*.1);
    r->sendTime[seq]=0;
    goto TRYAGAIN;
  }
  if (f==LINKTEST) {
    int i = pack->info;
    if (i>=0 && testLinkPkt<128) testLinkBuf[testLinkPkt++]=i;
    else if (i==-1) testLinkPkt=0;
    else if (i==-2) sendPack (*sock,LINKTEST,-3,testLinkBuf,testLinkPkt,*ir,UDP);
    else if (i<=-5) testLink(*sock,*ir,(int)pack->data[0]);
    goto TRYAGAIN;
  }
  if (s>=0) {
#if DEBUGRESEQ
    if (pack->try<3 && (s==20 || s==60 || s==100)) goto TRYAGAIN;
#endif
    pack->func=RECEIPT;
    rstatus = sendto (*sock, pack, 8, 0, (struct sockaddr *)r->addr, addrlen_t);
    if (rstatus<=0) { printf("SockRcptStatus = %d of %d\n",rstatus,8); }
    pack->func=f;
    pack->seq=s;
  }
  if (pack->try>0) { /* if retry - check to see if we already received it */
    rdpmin=rdpin-100; if (rdpmin<0) rdpmin=0;
    for (i=rdpin; i>=rdpmin; i--) { j = (i&0x7f);
      if (s == recvseq[j] && r->addr[0]==recvaddrs[j*2]
		&& r->addr[1]==recvaddrs[j*2+1]) goto TRYAGAIN;
    }
  }
  if (s>=0) {
    /* store record to check against retries */
    j = (rdpin++)&0x7f;
    recvseq[j]=s;
    recvaddrs[j*2]=r->addr[0]; recvaddrs[j*2+1]=r->addr[1];
    r->recvSeq++;
    if (s!=r->recvChk+1 && s!=r->recvChk-127 && r->state==OPENED) {
      /* handle packets recieved out-of-order */
      off = r->recvOff; if (off+len>MAXBUF) off=0;
      memcpy ((void*)(r->recvBuffer+off), (void*)pack, len);
      r->recvOffset[seq]=off; r->recvLength[seq]=len;
      r->recvTime[seq]=gtime; r->recvQueue++;
      r->recvOff = off+len;
      /* sendLinkProblemReport (ir,"PKTORDER Seq=%d Chk=%d",s,r->recvChk,pack,len); */
      goto TRYAGAIN;
    }
    r->recvChk=s;
  }
  return status-8;
}

int checkRecvQueue (int *sock, packetStruct *pack, int *length, int *ir, int *flags) {
  int off,len,seq,oseq,status=0,ochk=0;
  remoteStruct *r = remote+(*ir);
  seq = r->recvChk, oseq=-1;
  while (r->recvQueue>0) {
    seq++; seq = seq&0x7f;
    if (r->recvTime[seq]==0) {
      if (oseq<0) oseq=seq;
      if (++ochk>127) {
        sendLinkProblemReport (ir,"PKTRQCHK Queued= %d Seq= %d",r->recvQueue,seq,pack,0);
        r->recvQueue=0; return -1; /* something went wrong - reset */
      }
      continue; /* find next received packet */
    }
    if (oseq>=0) {
      if (gtime-r->recvTime[seq]<timeLost) return -1;
      pack->seq=seq; pack->func=0;
      sendLinkProblemReport (ir,"PKTLOST Seq= %d Out= %d",seq,oseq,pack,0);
      oseq=-1;
    }
    r->recvTime[seq]=0;
    off=r->recvOffset[seq]; len=r->recvLength[seq];
    memcpy ((void*)pack,(void*)(r->recvBuffer+off),len);
    r->recvChk=seq;
    r->recvReOrder++;
    r->recvQueue--;
    return len-8;
  }
  return -1;
}

int checkPacketQueue (int *sock, int *ir, int *flags) {
  int sent=0,status,i,seq,retry,off,len,addrlen=16;
  double t,rtd; remoteStruct *r; packetStruct *pack;
  r = remote+(*ir);
  for (i=r->sendChk; i<r->sendSeq; i++) {
    seq = i&0x7f;
    t = r->sendTime[seq];
    if (t==0) {
      if (i==r->sendChk) r->sendChk++;
      continue;
    }
    off = r->sendOffset[seq];
    len = r->sendLength[seq];
    pack = (packetStruct *)(r->sendBuffer+off);
    retry = r->sendBuffer[off+TRY];
    /* default initial wait until latency is measured */
    if (r->latency==0) rtd=1; else rtd=r->timeRetry+r->latency;
    if ((gtime-t)<(retry+1)*rtd) continue;
    if (t<0 && (i-r->sendChk) > r->maxWindow) continue;
    if (t>0 && ++retry>r->maxRetry) {
      r->sendTime[seq]=0;
      if (i==r->sendChk) r->sendChk++;
      r->sendDrop++;
      sendLinkProblemReport (ir,"PKTDROP Seq=%d Retry=%d",seq,retry,pack,len);
      continue;
    }
/*    if (retry>0) printf("Retry %d for f=%d seq=%d from %d at %lf\n",
	retry,pack->func,seq,myport,gtime); */
    if (t<0) r->sendTime[seq]=gtime;
    r->sendBuffer[off+TRY] = retry;
    if (*ir==0) memcpy ( (void*)r->addr, (void*)(r0addrs+seq*4), 16);
    retry=4; RETRY:
    status = sendto (*sock, r->sendBuffer+off,len, 0, (struct sockaddr *)r->addr, addrlen);
    /* JC4: MSZ MOD changed next line from status<=0 to status<0 since it is
            not an error if sentto(..) returns 0 (0=zero bytes sent, -1=error).
     */
    if (status<0) {
      printf("Retry send with status %d and len %d\n",status,len); perror("");
      if (errno==11 && --retry>0) { usleep(5); goto RETRY; } /* resource unavailable, try again */
      printf("SockSendStatus = %d of %d\n",status,len); perror("");
    }
    sent++;
  }
  return sent;
}

int checkRemotes (int *sock, packetStruct *pack, int *length, int *ir, int *flags) {
  int i,status; remoteStruct *r;
  for (i=0; i<=nremote; i++) {
    *ir = i;  r = remote+i;
    if (i>0 && r->recvQueue>0) {
      status = checkRecvQueue(sock,pack,length,ir,flags);
      if (status>=0) return status;
    }
    status = checkPacketQueue(sock,ir,flags);
  }
  return -1;
}

int sendLinkProblemReport (int *ir, char *text, int td1, int td2,
		packetStruct *pack, int len) {
  char msgtext[80];
  int lt; /* length of msgtext */
  lt = snprintf(msgtext,80, text,td1,td2);
  /* sprintf(msgtext,text,td1,td2); lt=strlen(msgtext); TODO: remove */
  sendMessage (msgtext,&lt,ir,pack,&len);
  return 0;
}

int formAddr (const char *hostname, int *port, struct sockaddr_in *address, fstring_len_t lhostname) {
  struct hostent *hostent;
  char host[lhostname+1]; /* +1 to have space for \0 char in C string */
  int  lhost;             /* length of host */

  strf2c(hostname,lhostname, host,lhostname+1, F2C_TRIM); /* convert Fortran string to C string */
  hostent = gethostbyname(host);
  if (hostent==NULL) {
    printf("rmif.formAddr(): gethostbyname returned null\n");
    return -1;
  }
  address->sin_family = hostent->h_addrtype;
  address->sin_port = htons(*port);
  memcpy(&address->sin_addr, hostent->h_addr, hostent->h_length);
  return 0;
}

int testLink (int sock, int ir, int mode) {
  int i,pktsize=1;
  char buf[32768];
  for (;mode<0; mode++) pktsize*=2;
  sendPack (sock,LINKTEST,-1,0,0,ir,UDP);
  usleep(500000);
  for (i=0; i<128; i++) sendPack (sock,LINKTEST,i,buf,pktsize,ir,UDP);
  usleep(500000);
  sendPack (sock,LINKTEST,-4,0,0,ir,UDP);
  return 0;
}

int compress (propMembStruct *prop, char *tmp, char *buf, int_4 *plen) {
  char bmax,bmin,bval,b;
  int_4 i,j,k,kk,n,m,i1,i2,ip,ipk,nband,nbh,imax,mode,len=*plen,lenchk=0;
  mode=prop->comp;
  i1 = max(0,min(len,prop->trim1));
  i2 = max(0,min(len,prop->trim2));
  if (i2==0) i2=len;
  nband = ((i2-i1)+(prop->plotwidth/2))/prop->plotwidth;
  for (;tmp[i1]==tmp[i1+1] && i1<len; i1++) {}
  if (i1<5) i1=0;
  for (;tmp[i2-2]==tmp[i2-1] && i2>i1; i2--) {}
  if (len-i2<5) i2=len;
  i=n=0;
  /* left edge constant */
  for (; i<i1; i+=k) {
    k=kk=i1-i; if (k>127) { kk = -k/128; k = -kk*128; }
    buf[n++] = DC_CONST;
    buf[n++] = kk;
    buf[n++] = tmp[i1];
    lenchk += k;
  }
  /* center data */
  for (;i<i2;) {
    k=kk=i2-i; if (k>127) { kk = -k/128; k = -kk*128; }
    if (prop->comp == 1) {
      buf[n++] = DC_RAW;
      buf[n++] = kk;
      memcpy (buf+n,tmp+i,k);
      n+=k; i+=k;
    }
    else if (nband>=3) {
      buf[n++] = DC_BAND;
      buf[n++] = kk;
      buf[n++] = nband; nbh = nband/2;
      for (ipk=i+k; i<ipk; ) {
        j=i+nband; if (j>ipk) j=ipk;
        ip=imax=i; bmax=bmin=tmp[i++];
        for (;i<j;) { b=tmp[i++];
          if (b>bmax) { bmax=b; imax=i-1; }
          else if (b<bmin) bmin=b;
        }
        if (imax-ip>=nbh) { buf[n++]=bmin; buf[n++]=bmax; }
        else 		  { buf[n++]=bmax; buf[n++]=bmin; }
      }
      i = ipk;
    }
    else {
      buf[n++] = DC_PACK;
      buf[n++] = kk;
      for (ipk=i+k; i<ipk; ) {
        ip=i; j=i+8; if (j>ipk) j=ipk;
        bmax=bmin=tmp[i++];
        for (;i<j;) {
          b=tmp[i++];
          if (b>bmax) bmax=b;
          else if (b<bmin) bmin=b;
	}
        buf[n++] = bmax;
        for (i=ip; i<j;) {
          b = lutl[bmax-tmp[i++]];
          if (i<j) b |= lutu[bmax-tmp[i++]];
          buf[n++] = b;
        }
      }
      i = ipk;
    }
    lenchk += k;
  }
  /* right edge constant */
  for (; i<len; i+=k) {
    k=kk=len-i; if (k>127) { kk = -k/128; k = -kk*128; }
    buf[n++] = DC_CONST;
    buf[n++] = kk;
    buf[n++] = tmp[i2];
    lenchk += k;
  }
  if (lenchk!=len) printf("Compression error %d of %d\n",lenchk,len);
  return n;
}

/**
 * Converts IPv4 socket address (struck sockaddr_in) to a string (char *) in
 * the following format "xxx.xxx.xxx.xxx:port"
 * @param sa  socket address (struct sockaddr_in)
 * @param str destination output string (must have enough space for IPv4 address
 *            and port). , i.e. len must be >= 22 otherwise it will be cutoff)
 * @returns length of output string (char *)
 * @since NeXtMidas 2.7.3
 */
int_4 addr_to_string_ (const struct sockaddr_in *sa, char *str, fstring_len_t lstr) {
  int_4 retstrlen = 0;
  const struct in_addr *sin_addr = &sa->sin_addr; /* in network byte-order */
  const unsigned char *addr = (void*) sin_addr;   /* cast to byte array to easily get IP address */
  u_int16_t port = ntohs(sa->sin_port);  /* convert from network byte-order to host */

  memset(str, ' ', lstr); /* clear ouput string for fortran */
  retstrlen = (int_4) snprintf(str, (size_t)lstr, "%u.%u.%u.%u:%d",
      addr[0], addr[1], addr[2], addr[3], port);

  return retstrlen;
}
