/*
 * dxlAPRS toolchain
 *
 * Copyright (C) Christian Rabler <oe5dxl@oevsv.at>
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */


#define X2C_int32
#define X2C_index32
#ifndef X2C_H_
#include "X2C.h"
#endif
#define udpgate4_C_
#ifndef udp_H_
#include "udp.h"
#endif
#ifndef tcp_H_
#include "tcp.h"
#endif
#ifndef osi_H_
#include "osi.h"
#endif
#include <osic.h>
#ifndef Select_H_
#include "Select.h"
#endif
#ifndef aprspos_H_
#include "aprspos.h"
#endif
#ifndef aprsstr_H_
#include "aprsstr.h"
#endif

/* axudp / tcpip  aprs-is gateway and message repeater by OE5DXL */
/*FROM Storage IMPORT ALLOCATE, DEALLOCATE; */
/*FROM TimeConv IMPORT time;  */
/*
FROM stat IMPORT fstat, stat_t;
*/
#define udpgate4_CALLLEN 7

#define udpgate4_HASHSIZE 65536

#define udpgate4_VERS "udpgate(c) 0.63"

#define udpgate4_TOCALL "APNL51"

#define udpgate4_HTTPTIMEOUT 60

#define udpgate4_APRSLISTENSOCKS 4
/* max same time tcp connects */

#define udpgate4_WWWLISTENSOCKS 16
/* max same time www tcp connects */

#define udpgate4_UDPSHOWTIME 600
/* show udp in html */

#define udpgate4_GATECONNDELAY 30

#define udpgate4_MAXINTERNALDELAY 5
/* max seconds between udp ports read to not discard data */

#define udpgate4_DEFAULTPONGTIME 30
/* s after last #ping stop datatransfer */

typedef char MONCALL[10];

typedef char FILENAME[1024];

typedef char FRAMEBUF[512];

typedef char FILTERST[256];

/*
      FRAMEBUF=ARRAY[0..256+3+11*10] OF CHAR;
*/
typedef uint32_t CHSET[4];


struct BEACON {
   uint32_t bintervall;
   FILENAME bfile;
   uint32_t btime;
};

struct POSCALL;


struct POSCALL {
   MONCALL call;
   char typ0;
   struct aprspos_POSITION pos;
};

struct FILTERS;


struct FILTERS {
   char typ0;
   struct aprspos_POSITION base;
   struct aprspos_POSITION edge;
   float radius;
   MONCALL viacalls[8];
   char notvia;
   MONCALL entrycalls[8];
   char notentry;
   MONCALL prefixes[8];
   char notprefix;
   MONCALL destcalls[8];
   char notdestcall;
   char typs[13];
   char nottyps;
};

typedef char WWWB[1401];

struct WWWBUF;

typedef struct WWWBUF * pWWWBUF;


struct WWWBUF {
   WWWB buf;
   int32_t tlen;
   char push;
   pWWWBUF next;
};

struct QWatch;


struct QWatch {
   uint32_t lasttb;
   int32_t qsize;
   int32_t lastqb;
   int32_t txbyte[60];
};

struct TCPSOCK;

typedef struct TCPSOCK * pTCPSOCK;


struct TCPSOCK {
   pTCPSOCK next;
   int32_t fd;
   uint32_t beacont;
   uint32_t connt;
   uint32_t pongtime;
   char slowlink;
   char valid;
   char pingout; /* ping timeout state */
   char service;
   uint32_t gatepri;
   char ipnum[64];
   char port[6];
   struct POSCALL user;
   char vers[21];
   uint32_t wwwst;
   uint32_t reload;
   char sortby[2];
   struct QWatch qwatch;
   uint32_t txframes;
   uint32_t txbytes;
   uint32_t txbytesh;
   uint32_t rxframes;
   uint32_t rxbytes;
   uint32_t rxbytesh;
   uint32_t losttxframes;
   uint32_t lostrxframes;
   struct FILTERS filters;
   FILTERST outfilterst; /* for www show outconn filters */
   int32_t rpos;
   int32_t tlen;
   FRAMEBUF rbuf;
   char tbuf[1024];
   char get[256];
   pWWWBUF txbuf;
};

struct UDPSOCK;

typedef struct UDPSOCK * pUDPSOCK;

struct _0;


struct _0 {
   uint32_t uip;
   uint32_t uport;
   uint32_t rxframes;
   uint32_t rxbytes;
   uint32_t utime;
   uint32_t dtime;
};


struct UDPSOCK {
   pUDPSOCK next;
   int32_t fd;
   aprsstr_GHOSTSET ghosts;
   char rawread;
   char checkip;
   uint32_t ip;
   uint32_t bindport;
   uint32_t dport;
   uint32_t lasttxtime; /* for net to rf bit/s limit */
   uint32_t laststat;
   float torfradius; /* radius 0 no gateing */
   uint32_t maxbytes; /* byte/s tx */
   uint32_t lasttxbytes; /* for tx byte/s limit */
   uint32_t txframes;
   uint32_t txbytes;
   char allpathkey[10]; /* keyword on comment for allpath */
   char portname[10];
   struct _0 stat[16];
};

enum GATEFILT {udpgate4_gUNGATE, udpgate4_gRFONLY, udpgate4_gNOGATE,
                udpgate4_gTCPIP, udpgate4_gTCPXX, udpgate4_gQ};


#define udpgate4_MAXHEARD 500

#define udpgate4_cUSERMSG ":"

#define udpgate4_cTHIRDPARTY "}"

#define udpgate4_ICONDIR "icon"

#define udpgate4_CALLINKFN "calllink.txt"
/* build an URL for a klicked mh call */

#define udpgate4_SERVERLINKFN "serverlink.txt"
/* build an URL for a klicked server ip */

#define udpgate4_MHSTEPS 48

#define udpgate4_tCELSIUS "C"

#define udpgate4_tSPEED "S"

#define udpgate4_UNACKRET 1

#define udpgate4_MSGLEN 67

typedef char MSGTEXT[68];

typedef char ACKTEXT[5];

typedef char REPLYACK[2];

enum SOURCE {udpgate4_OBSOLET, udpgate4_NET, udpgate4_INDIR, udpgate4_DIR};


struct MESSAGE;

typedef struct MESSAGE * pMESSAGE;


struct MESSAGE {
   pMESSAGE next;
   uint32_t chkt;
   uint32_t txtime;
   uint32_t gentime;
   uint32_t txport;
   uint32_t msg2rf;
   uint32_t msg2net;
   uint32_t ack2rf;
   uint32_t ack2net;
   uint32_t retryc;
   uint8_t acksrc;
   uint8_t src;
   char reject;
   char acked;
   char ackackt;
   char queryrep;
   MONCALL from;
   MONCALL to;
   ACKTEXT ack;
   REPLYACK replyack;
   MSGTEXT text; /* no text is ack */
};

struct HEARD;

typedef struct HEARD * pHEARD;


struct HEARD {
   pHEARD next;
   uint32_t fromrx;
   uint32_t time0;
   MONCALL call;
   uint32_t cntt;
   struct aprspos_POSITION position;
   char sym;
   char symt;
   uint16_t cnt[49];
   char head[41];
   char datatyp;
   float data;
   double sortval; /* sort value inserted depending on sort by */
   char ungate; /* flag set by user to not igate this direct heard */
   uint16_t txd;
   uint8_t quali;
   signed char level;
};

static uint32_t msgsendtime;

static uint32_t udpgate4_POLYNOM = 0x8408UL;

static uint32_t udpgate4_CRCINIT = 0xFFFFUL;

static uint32_t udpgate4_CRCRESULT = 0x9F0BUL;

#define udpgate4_CR "\015"

#define udpgate4_LF "\012"

#define udpgate4_cUSERMESSAGE ":"

#define udpgate4_cISGATEWAY "G"

#define udpgate4_cISSERVER "S"

#define udpgate4_cISWWW "W"

#define udpgate4_cTELEMETRY "T"

static char mhperport;

/* mh line for same call but different port */
static char datafilter;

static char verb;

/* send no data to user with no filter set */
static char callsrc;

static pTCPSOCK tcpsocks;

static uint32_t showip1;

static uint32_t showport1;

static uint32_t systime;

static uint32_t keeptime;

static char ungates[6][11];

static uint32_t timehash[65536];

static uint32_t realtime;

static uint32_t udpdonetime;

static uint32_t maxpongtime;

static uint32_t dupetime;

static uint32_t uptime;

static FILENAME rawlogname;

static FILENAME netbeaconfn;

static FILENAME logframename;

static uint32_t lastdnstime;

static uint32_t gateconndelay;

static uint32_t netbeaconintervall;

static uint32_t vcourse;

static uint32_t vspeed;

static uint32_t qas;

static uint32_t qasc;

static uint32_t maxusers;

static uint32_t httpcount;

static char vsym;

static char vsymt;

static int32_t valt;

static int32_t wwwsizelimit;

static int32_t logframes;

static MONCALL showid;

static MONCALL servercall;

static struct aprspos_POSITION home; /* own position */

static char passwd[6];

static char serverrangefilter[256];

static char actfilter[256];

static char wwwdir[1024];

static char tcpbindport[6];

static char wwwbindport[6];

static char nettorfpath[81];

static pUDPSOCK udpsocks;

static uint32_t trygate;

static int32_t qmaxtime;

static FILENAME gatesfn; /* filename with gateway table */

struct _1;


struct _1 {
   char url[256];
   char port[6];
   uint32_t resolvtime;
   uint32_t connecttime;
   FILTERST filterst;
};

static struct _1 gateways[21];

static MONCALL rfdestcall;

static MONCALL netdestcall;

static MONCALL viacall;

static uint32_t netmhin;

static uint32_t netmhout;

static uint32_t msghttpcount;

static uint32_t mhhttpcount;

static uint32_t maxmsg;

static uint32_t maxatonce;

static uint32_t lastrfsent;

static uint32_t purgeunack;

static uint32_t purgemsg;

static uint32_t purgeacked;

static uint32_t purgeunacksent;

static uint32_t rfquiet;

static uint32_t heardtimevia;

static uint32_t heardtimew;

static uint32_t heardtimetcp;

static uint32_t heardtime;

static pMESSAGE messages;

static pHEARD heardvia;

static pHEARD hearddir;

static pHEARD heardtcp;

static char sendnetmsg;

static MONCALL netmh[100];

static uint32_t mhfilelines;

static FILENAME mhfilename;

static char udp2[100];


static void spintime(void)
{
   /* make monotonic systime out of jumping realtime */
   uint32_t dt;
   uint32_t t;
   t = osic_time();
   dt = t-realtime;
   realtime = t;
   if (dt<60UL) systime += dt;
} /* end spintime() */

/*
PROCEDURE Rename(fname,newname: ARRAY OF CHAR);
VAR ok:BOOLEAN;
BEGIN
  FileSys.Rename(fname, newname, ok);
END Rename;
*/

static void Err(const char text[], uint32_t text_len)
{
   osi_WrStr("udpgate: ", 10ul);
   osi_WrStr(text, text_len);
   osi_WrStrLn(" error abort", 13ul);
   X2C_ABORT();
} /* end Err() */


static uint32_t Max(uint32_t a, uint32_t b)
{
   if (a>b) return a;
   else return b;
   return 0;
} /* end Max() */


static uint32_t Min(uint32_t a, uint32_t b)
{
   if (a<b) return a;
   else return b;
   return 0;
} /* end Min() */


static void skipblank(const char s[], uint32_t s_len, uint32_t * p)
{
   while (*p<s_len-1 && s[*p]==' ') ++*p;
} /* end skipblank() */


static void app(char s[], uint32_t s_len, float x, char rad)
{
   char h[30];
   aprsstr_Append(s, s_len, "/", 2ul);
   if (rad) aprsstr_FixToStr(X2C_DIVR(x,1.7453292519444E-2f), 5UL, h, 30ul);
   else aprsstr_FixToStr(x, 0UL, h, 30ul);
   aprsstr_Append(s, s_len, h, 30ul);
} /* end app() */


static void wrcalls(char s[], uint32_t s_len, const MONCALL tab[],
                uint32_t tab_len, char not, char sym)
{
   uint32_t j;
   if (tab[0UL][0U]) {
      if (s[0UL]) aprsstr_Append(s, s_len, " ", 2ul);
      if (not) aprsstr_Append(s, s_len, "-", 2ul);
      aprsstr_Append(s, s_len, (char *) &sym, 1u/1u);
      j = 0UL;
      while (tab[j][0U]) {
         aprsstr_Append(s, s_len, "/", 2ul);
         aprsstr_Append(s, s_len, tab[j], 10ul);
         ++j;
      }
   }
} /* end wrcalls() */


static void FiltToStr(struct FILTERS f, char s[], uint32_t s_len)
{
   struct FILTERS * anonym;
   s[0UL] = 0;
   { /* with */
      struct FILTERS * anonym = &f;
      if (anonym->typ0=='a') {
         aprsstr_Assign(s, s_len, "a", 2ul);
         app(s, s_len, anonym->base.lat, 1);
         app(s, s_len, anonym->base.long0, 1);
         app(s, s_len, anonym->edge.lat, 1);
         app(s, s_len, anonym->edge.long0, 1);
      }
      else if (anonym->typ0=='r') {
         aprsstr_Assign(s, s_len, "r", 2ul);
         app(s, s_len, anonym->base.lat, 1);
         app(s, s_len, anonym->base.long0, 1);
         app(s, s_len, anonym->radius, 0);
      }
      else if (anonym->typ0=='m') {
         aprsstr_Assign(s, s_len, "m", 2ul);
         app(s, s_len, anonym->radius, 0);
      }
      wrcalls(s, s_len, anonym->viacalls, 8ul, anonym->notvia, 'd');
      wrcalls(s, s_len, anonym->entrycalls, 8ul, anonym->notentry, 'e');
      wrcalls(s, s_len, anonym->prefixes, 8ul, anonym->notprefix, 'p');
      wrcalls(s, s_len, anonym->destcalls, 8ul, anonym->notdestcall, 'u');
      if (anonym->typs[0U]) {
         aprsstr_Append(s, s_len, " ", 2ul);
         if (anonym->nottyps) aprsstr_Append(s, s_len, "-", 2ul);
         aprsstr_Append(s, s_len, "t/", 3ul);
         aprsstr_Append(s, s_len, anonym->typs, 13ul);
      }
   }
} /* end FiltToStr() */


static char Watchclock(uint32_t * t, uint32_t intervall)
{
   uint32_t tn;
   if (intervall>0UL || *t==0UL) {
      /* send once */
      tn = systime;
      if (*t<=tn) {
         *t += intervall;
         if (*t<=tn) *t = tn+intervall;
         return 1;
      }
   }
   return 0;
} /* end Watchclock() */

static uint32_t udpgate4_ROOT = 0x73E2UL;


static uint32_t call2pass(const char c[], uint32_t c_len)
{
   uint32_t i0;
   uint32_t s;
   s = 0x73E2UL;
   i0 = 0UL;
   while ((i0<=c_len-1 && c[i0]) && c[i0]!='-') {
      s = s^X2C_LSH((uint32_t)(uint32_t)(uint8_t)X2C_CAP(c[i0]),32,
                (int32_t)!(i0&1)*8L);
      ++i0;
   }
   return (uint32_t)(s&0x7FFFUL);
} /* end call2pass() */


static int32_t GetSec(char h[], uint32_t h_len, uint32_t * p,
                uint32_t * n)
{
   char ok0;
   int32_t GetSec_ret;
   X2C_PCOPY((void **)&h,h_len);
   h[h_len-1] = 0;
   *n = 0UL;
   ok0 = 0;
   while ((uint8_t)h[*p]>='0' && (uint8_t)h[*p]<='9') {
      ok0 = 1;
      *n = ( *n*10UL+(uint32_t)(uint8_t)h[*p])-48UL;
      ++*p;
   }
   if (!ok0) {
      GetSec_ret = -1L;
      goto label;
   }
   /*
   IO.WrCard(n,10); IO.WrLn;
   */
   GetSec_ret = 0L;
   label:;
   X2C_PFREE(h);
   return GetSec_ret;
} /* end GetSec() */


static char callok(const char h[], uint32_t h_len)
{
   uint32_t lit;
   uint32_t num;
   uint32_t i0;
   char c;
   num = 0UL;
   lit = 0UL;
   i0 = 0UL;
   for (;;) {
      c = h[i0];
      if ((uint8_t)c>='0' && (uint8_t)c<='9') ++num;
      else if ((uint8_t)c>='A' && (uint8_t)c<='Z') ++lit;
      else break;
      ++i0;
   }
   if ((lit<2UL || num==0UL) || num>2UL) return 0;
   if (h[i0]=='-') {
      ++i0;
      if (h[i0]=='1') {
         ++i0;
         if ((uint8_t)h[i0]>='0' && (uint8_t)h[i0]<='5') ++i0;
      }
      else {
         if ((uint8_t)h[i0]<'1' || (uint8_t)h[i0]>'9') return 0;
         ++i0;
      }
   }
   return h[i0]==0;
} /* end callok() */


static void readurlsfile(const char gatesfn0[], uint32_t gatesfn_len)
{
   uint32_t i0;
   uint32_t n;
   int32_t ii;
   int32_t len;
   int32_t fd;
   FILENAME h;
   memset((char *)gateways,(char)0,sizeof(struct _1 [21]));
   fd = osi_OpenRead(gatesfn0, gatesfn_len);
   if (fd<0L) {
      strncpy(h,"-g :file <",1024u);
      aprsstr_Append(h, 1024ul, gatesfn0, gatesfn_len);
      aprsstr_Append(h, 1024ul, "> not readable", 15ul);
      osi_WrStrLn(h, 1024ul);
      return;
   }
   n = 0UL;
   do {
      i0 = 0UL;
      for (;;) {
         len = osi_RdBin(fd, (char *) &h[i0], 1u/1u, 1UL);
         if (((len<=0L || i0>=1023UL) || h[i0]=='\015') || h[i0]=='\012') {
            h[i0] = 0;
            break;
         }
         ++i0;
      }
      if (h[0U] && h[0U]!='#') {
         if (h[0U]=='[') {
            ii = 1L;
            while (h[ii] && h[ii]!=']') ++ii;
            if (h[ii]!=']' || h[ii+1L]!=':') {
               osi_WrStrLn("urlfile: [url]:port", 20ul);
            }
            h[ii] = 0;
            i0 = 1UL;
            while (i0<=1023UL) {
               h[i0-1UL] = h[i0];
               ++i0;
            }
         }
         else ii = aprsstr_InStr(h, 1024ul, ":", 2ul);
         if (ii>=0L) h[ii] = 0;
         aprsstr_Assign(gateways[n].url, 256ul, h, 1024ul);
         if (ii>0L) {
            /* port number */
            ++ii;
            i0 = 0UL;
            while (ii<=1023L) {
               h[i0] = h[ii];
               ++i0;
               ++ii;
            }
         }
         else osi_WrStrLn("urlfile: [url]:port", 20ul);
         ii = aprsstr_InStr(h, 1024ul, "#", 2ul);
         if (ii>=0L) h[ii] = 0;
         if (h[0U]==0) {
            osi_WrStrLn("urlfile: [url]:port#filters", 28ul);
         }
         aprsstr_Assign(gateways[n].port, 6ul, h, 1024ul);
         if (ii>0L) {
            /* we have a filter string */
            ++ii;
            i0 = 0UL;
            while (ii<=1023L) {
               if (h[ii]==',') h[ii] = ' ';
               h[i0] = h[ii];
               ++i0;
               ++ii;
            }
            aprsstr_Assign(gateways[n].filterst, 256ul, h, 1024ul);
         }
         ++n;
      }
   } while (!(len<=0L || n>20UL));
   osic_Close(fd);
} /* end readurlsfile() */

static aprsstr_GHOSTSET _cnst = {0x00000000UL,0x00000000UL,0x00000000UL,
                0x00000000UL,0x00000000UL,0x00000000UL,0x00000000UL,
                0x00000000UL,0x00000000UL};

static void parms(void)
{
   char h[4096];
   aprsstr_GHOSTSET ghost;
   char err;
   char lasth;
   uint32_t n;
   uint32_t i0;
   uint32_t gatecnt;
   int32_t len;
   int32_t fd;
   pUDPSOCK ush;
   pUDPSOCK usock;
   int32_t ii;
   char allkey[10];
   struct UDPSOCK * anonym;
   uint32_t tmp;
   err = 0;
   verb = 0;
   datafilter = 0;
   gatecnt = 0UL;
   memcpy(ghost,_cnst,36u);
   keeptime = 0UL; /*600*/ /* default keep connected to gateway time */
   allkey[0U] = 0;
   for (;;) {
      osi_NextArg(h, 4096ul);
      if (h[0U]==0) break;
      if ((h[0U]=='-' && h[1U]) && h[2U]==0) {
         lasth = h[1U];
         if (lasth=='R' || lasth=='M') {
            osi_NextArg(h, 4096ul);
            osic_alloc((char * *) &usock, sizeof(struct UDPSOCK));
            if (usock==0) Err("out of memory", 14ul);
            memset((char *)usock,(char)0,sizeof(struct UDPSOCK));
            { /* with */
               struct UDPSOCK * anonym = usock;
               if (aprsstr_GetIp2(h, 4096ul, &anonym->ip, &anonym->dport,
                &anonym->bindport, &anonym->checkip)<0L) {
                  Err("-R or -M need ip:port:port", 27ul);
               }
               anonym->fd = openudp();
               anonym->rawread = lasth=='R';
               if (anonym->fd<0L || bindudp(anonym->fd,
                anonym->bindport)<0L) {
                  Err("-R or -M cannot bind udpport", 29ul);
               }
               len = socknonblock(anonym->fd);
               ii = aprsstr_InStr(h, 4096ul, "+", 2ul);
               if (ii>0L) {
                  i0 = (uint32_t)(ii+1L);
                  if (GetSec(h, 4096ul, &i0, &n)>=0L) {
                     anonym->maxbytes = n;
                     ++i0;
                     if (h[i0-1UL]==':' && GetSec(h, 4096ul, &i0, &n)>=0L) {
                        anonym->torfradius = (float)n;
                     }
                  }
               }
               ii = aprsstr_InStr(h, 4096ul, "#", 2ul); /* get port name */
               if (ii>0L) {
                  ++ii;
                  i0 = 0UL;
                  while ((i0<=9UL && ii<=4095L) && h[ii]) {
                     anonym->portname[i0] = h[ii];
                     ++i0;
                     ++ii;
                  }
               }
               memcpy(anonym->ghosts,ghost,36u);
               aprsstr_Assign(anonym->allpathkey, 10ul, allkey, 10ul);
               anonym->next = 0;
            }
            if (udpsocks==0) udpsocks = usock;
            else {
               ush = udpsocks;
               while (ush->next) ush = ush->next;
               ush->next = usock;
            }
            memcpy(ghost,_cnst,36u);
         }
         else if (lasth=='c') callsrc = 1;
         else if (lasth=='s' || lasth=='S') {
            osi_NextArg(h, 4096ul);
            aprsstr_Assign(servercall, 10ul, h, 4096ul);
            if ((servercall[0U]==0 || servercall[0U]=='-')
                || lasth=='s' && !callok(h, 4096ul)) {
               Err("-s call-ssid", 13ul);
            }
         }
         else if (lasth=='p') {
            osi_NextArg(h, 4096ul);
            if ((uint8_t)h[0U]>='0' && (uint8_t)h[0U]<='9') {
               i0 = 0UL;
               while (i0<=5UL && (uint8_t)h[i0]>=' ') {
                  passwd[i0] = h[i0];
                  ++i0;
               }
            }
            else {
               fd = osi_OpenRead(h, 4096ul);
               if (fd<0L) Err("-p passcode or passwordfile", 28ul);
               len = osi_RdBin(fd, (char *)passwd, 6u/1u, 5UL);
               if (len>=0L) passwd[len] = 0;
               else Err("-p error with password file", 28ul);
               osic_Close(fd);
            }
            passwd[5U] = 0;
            i0 = 0UL;
            while ((uint8_t)passwd[i0]>='0' && (uint8_t)passwd[i0]<='9') {
               ++i0;
            }
            if (i0==0UL || passwd[i0]) Err("-p invalid passcode", 20ul);
         }
         else if (lasth=='m') {
            osi_NextArg(h, 4096ul);
            i0 = 0UL;
            if (GetSec(h, 4096ul, &i0, &n)>=0L) maxusers = n;
            else Err("-m number", 10ul);
         }
         else if (lasth=='D') {
            osi_NextArg(h, 4096ul);
            aprsstr_Assign(wwwdir, 1024ul, h, 4096ul);
            if (h[0U]==0 || h[0U]=='-') Err("-D directory", 13ul);
            i0 = aprsstr_Length(wwwdir, 1024ul);
            if (i0>0UL && wwwdir[i0-1UL]!='/') {
               aprsstr_Append(wwwdir, 1024ul, "/", 2ul);
            }
         }
         else if (lasth=='d') {
            osi_NextArg(h, 4096ul);
            i0 = 0UL;
            if (GetSec(h, 4096ul, &i0, &n)>=0L) dupetime = n;
            else Err("-d number", 10ul);
            if (dupetime<27UL) {
               osi_WrStrLn("-d do not set dupefilter less 27s!", 35ul);
            }
         }
         else if (lasth=='e') {
            osi_NextArg(h, 4096ul);
            i0 = 0UL;
            if (GetSec(h, 4096ul, &i0, &n)>=0L) {
               if (n==0UL) n = 1UL;
               gateconndelay = n;
            }
            else Err("-e seconds", 11ul);
         }
         else if (lasth=='C') {
            osi_NextArg(h, 4096ul);
            i0 = 0UL;
            if (GetSec(h, 4096ul, &i0, &n)>=0L) heardtimetcp = n*60UL;
            else Err("-C minutes", 11ul);
         }
         else if (lasth=='H') {
            osi_NextArg(h, 4096ul);
            i0 = 0UL;
            if (GetSec(h, 4096ul, &i0, &n)>=0L) heardtimew = n*60UL;
            else Err("-H minutes", 11ul);
         }
         else if (lasth=='I') {
            osi_NextArg(h, 4096ul);
            i0 = 0UL;
            if (GetSec(h, 4096ul, &i0, &n)>=0L) heardtimevia = n*60UL;
            else Err("-I minutes", 11ul);
         }
         else if (lasth=='i') {
            osi_NextArg(h, 4096ul);
            if (h[0U]=='-') h[0U] = 0;
            aprsstr_Assign(allkey, 10ul, h, 4096ul);
         }
         else if (lasth=='L') {
            osi_NextArg(h, 4096ul);
            i0 = 0UL;
            if (GetSec(h, 4096ul, &i0, &n)>=0L) maxmsg = n;
            else Err("-L number", 10ul);
         }
         else if (lasth=='l') {
            osi_NextArg(h, 4096ul);
            i0 = 0UL;
            if (GetSec(h, 4096ul, &i0, &n)>=0L && h[i0]==':') {
               logframes = (int32_t)n;
               ++i0;
               for (n = 0UL; n<=1023UL; n++) {
                  if (i0<=4095UL) {
                     logframename[n] = h[i0];
                     ++i0;
                  }
               } /* end for */
               if ((uint8_t)logframename[0U]<=' ') {
                  Err("-l loglevel:filename", 21ul);
               }
            }
            else Err("log format is level:file", 25ul);
         }
         else if (lasth=='r') {
            osi_NextArg(rawlogname, 1024ul);
            if ((uint8_t)rawlogname[0U]<=' ') {
               Err("-r rawlogfilename", 18ul);
            }
         }
         else if (lasth=='F') {
            osi_NextArg(h, 4096ul);
            i0 = 0UL;
            if (GetSec(h, 4096ul, &i0, &n)>=0L && h[i0]==':') {
               mhfilelines = n;
               ++i0;
               for (n = 0UL; n<=1023UL; n++) {
                  if (i0<=4095UL) {
                     mhfilename[n] = h[i0];
                     ++i0;
                  }
               } /* end for */
               if ((uint8_t)mhfilename[0U]<=' ') {
                  Err("-F MHfilename", 14ul);
               }
            }
            else Err("MH File lines:file", 19ul);
         }
         else if (lasth=='n') {
            osi_NextArg(h, 4096ul);
            i0 = 0UL;
            if (GetSec(h, 4096ul, &i0, &n)>=0L && h[i0]==':') {
               netbeaconintervall = n*60UL;
               ++i0;
               for (n = 0UL; n<=1023UL; n++) {
                  if (i0<=4095UL) {
                     netbeaconfn[n] = h[i0];
                     ++i0;
                  }
               } /* end for */
               if ((uint8_t)netbeaconfn[0U]<=' ') {
                  Err("-n netbeacon filename", 22ul);
               }
            }
            else Err("-n netbeacon format is minutes:file", 36ul);
         }
         else if (lasth=='N') sendnetmsg = 0;
         else if (lasth=='t') {
            osi_NextArg(h, 4096ul);
            aprsstr_Assign(tcpbindport, 6ul, h, 4096ul);
            if (h[0U]==0 || h[0U]=='-') Err("-t port", 8ul);
         }
         else if (lasth=='w') {
            osi_NextArg(h, 4096ul);
            aprsstr_Assign(wwwbindport, 6ul, h, 4096ul);
            if (h[0U]==0 || h[0U]=='-') Err("-w port", 8ul);
         }
         else if (lasth=='f') {
            osi_NextArg(h, 4096ul);
            aprsstr_Assign(serverrangefilter, 256ul, h, 4096ul);
            if (h[0U]==0 || h[0U]=='-') Err("-f rangefilter", 15ul);
            tmp = aprsstr_Length(serverrangefilter, 256ul);
            i0 = 0UL;
            if (i0<=tmp) for (;; i0++) {
               if (serverrangefilter[i0]==',') serverrangefilter[i0] = ' ';
               if (i0==tmp) break;
            } /* end for */
         }
         else if (lasth=='g') {
            /* "url port" or "url:port" or "url:port#filter" */
            osi_NextArg(h, 4096ul);
            if (h[0U]==0) Err("-g url port", 12ul);
            if (h[0U]==':') {
               /* get urls later from file */
               for (i0 = 1UL; i0<=4095UL; i0++) {
                  h[i0-1UL] = h[i0];
               } /* end for */
               h[4095U] = 0;
               aprsstr_Assign(gatesfn, 1024ul, h, 4096ul);
            }
            else {
               if (gatecnt>20UL) Err("-g gateway table full", 22ul);
               h[4095U] = 0;
               if (h[0U]=='[') {
                  ii = 1L;
                  while (h[ii] && h[ii]!=']') ++ii;
                  if (h[ii]!=']' || h[ii+1L]!=':') {
                     Err("-g [url]:port", 14ul);
                  }
                  h[ii] = 0;
                  i0 = 1UL;
                  while (i0<=4095UL) {
                     h[i0-1UL] = h[i0];
                     ++i0;
                  }
               }
               else ii = aprsstr_InStr(h, 4096ul, ":", 2ul);
               if (ii>=0L) h[ii] = 0;
               aprsstr_Assign(gateways[gatecnt].url, 256ul, h, 4096ul);
               if (ii>0L) {
                  /* port number */
                  ++ii;
                  i0 = 0UL;
                  while (ii<=4095L) {
                     h[i0] = h[ii];
                     ++i0;
                     ++ii;
                  }
               }
               else osi_NextArg(h, 4096ul);
               h[4095U] = 0;
               ii = aprsstr_InStr(h, 4096ul, "#", 2ul);
               if (ii>=0L) h[ii] = 0;
               if (h[0U]==0) Err("-g url:port", 12ul);
               aprsstr_Assign(gateways[gatecnt].port, 6ul, h, 4096ul);
               if (ii>0L) {
                  /* we have a filter string */
                  ++ii;
                  i0 = 0UL;
                  while (ii<=4095L) {
                     if (h[ii]==',') h[ii] = ' ';
                     h[i0] = h[ii];
                     ++i0;
                     ++ii;
                  }
                  aprsstr_Assign(gateways[gatecnt].filterst, 256ul, h,
                4096ul);
               }
               ++gatecnt;
            }
         }
         else {
            if (lasth=='h') {
               osi_WrStrLn(" -0             send no Data (only Messages and a\
ck) to User with no Filter", 76ul);
               osi_WrStrLn(" -C <time>      connected (tcp) remember position\
 minutes (Min) (-C 1440)", 74ul);
               osi_WrStrLn(" -c             delete frames with no valid sourc\
e call in APRS-IS stream", 74ul);
               osi_WrStrLn(" -D <path>      www server root directory (-D /us\
r/www/)", 57ul);
               osi_WrStrLn(" -d <time>      dupe filter time in seconds, not \
below 27s! (default 60s)", 74ul);
               osi_WrStrLn(" -e <time>      wait before (re)connect to (next)\
 gateway in seconds, (30s)", 76ul);
               osi_WrStrLn(" -F <lines>:<file> write direct heard file (call,\
sym,port,s,cnt,km,data,path)", 78ul);
               osi_WrStrLn(" -f <filters>   backstream filter text sent to ou\
t connected server -f m/50", 76ul);
               osi_WrStrLn("                if blanks dont pass parameter set\
tings use , (-f m/30,-d/CW)", 77ul);
               osi_WrStrLn(" -g <url>:<port>[#<filters>] connect to APRS-IS g\
ateway, repeat -g for a list", 78ul);
               osi_WrStrLn("                with favorites first and all urls\
 will be tried to connect", 75ul);
               osi_WrStrLn("                if the active connect is not the \
first in list, urls", 69ul);
               osi_WrStrLn("                before will be polled and if gets\
 connected, data transfer", 75ul);
               osi_WrStrLn("                is switched to this link and the \
old gets disconnected", 71ul);
               osi_WrStrLn("                if no filter setting, global -f f\
ilter is used", 63ul);
               osi_WrStrLn("                -g www.db0anf.de:14580#m/50 -g 12\
7.0.0.1:3000", 62ul);
               osi_WrStrLn("                ipv6 if enabled by kernel -g [::1\
]:14580#m/200", 63ul);
               osi_WrStrLn(" -g :<filename> read gateway urls from file url:p\
ort#filter,filter,...", 71ul);
               osi_WrStrLn(" -h             this", 21ul);
               osi_WrStrLn(" -H <time>      direct heard keep time minutes (M\
in) (-H 1440)", 63ul);
               osi_WrStrLn(" -I <time>      indirect heard keep time minutes \
(Min) (-I 30)", 63ul);
               osi_WrStrLn(" -i <word>      keyword in rf-frame-comment to en\
able multipath, * for all frames", 82ul);
               osi_WrStrLn(" -j <time>      maximum time to (re)send messages\
 (s) (-j 43200)", 65ul);
               osi_WrStrLn(" -k <time>      0 always connect to gateway else \
connect on demand and hold (0)", 80ul);
               osi_WrStrLn("                (seconds) after last User gone or\
 valid UDP Data arrived", 73ul);
               osi_WrStrLn(" -L <number>    max messages stored else delete o\
ldest (-L 1000)", 65ul);
               osi_WrStrLn("                -L 0 and -x set and net-to-rf ena\
bled all msg to heard gatet", 77ul);
               osi_WrStrLn(" -l <level>:<file> logfile -l 6:/tmp/log.txt",
                45ul);
               osi_WrStrLn("                level: 1 logins, 2 +sent frames, \
6 +fitered frames, 7 +dups", 76ul);
               osi_WrStrLn(" -M <ip>:<dport>/<lport>[+<byte/s>[:<radius>]][#<\
portname>]", 60ul);
               osi_WrStrLn("                udp rf port (monitor frame format\
) for local (t)rx", 67ul);
               osi_WrStrLn("                <dport>/<lport> \"/\" only from t\
his ip, dport=0 no tx", 69ul);
               osi_WrStrLn("                +byte/s enable inet to rf for ser\
vices like WLNK, WHO-IS", 73ul);
               osi_WrStrLn("                :radius enable all inet to rf gat\
e (from km around digi)", 73ul);
               osi_WrStrLn("                messages to NOT direct heard user\
s are gated at any radius >0", 78ul);
               osi_WrStrLn("                if no <ip> given then \'127.0.0.1\\
' is used", 58ul);
               osi_WrStrLn("                #portname max 10 char like \'1448\
00\'", 52ul);
               osi_WrStrLn("                repeat -M for each radio port wit\
h a tx or different portname", 78ul);
               osi_WrStrLn(" -m <maxconnects> max inbound connects -m 20 (def\
ault 50)", 58ul);
               osi_WrStrLn(" -N             send no stored messages to net ex\
cept query answers", 68ul);
               osi_WrStrLn(" -n <min>:<file> netbeacon minutes:filename -n 10\
:netbeacon.txt", 64ul);
               osi_WrStrLn("                \\\\z ddhhmm, \\\\h hhmmss, \\\\:\
filename: insert file, \\\\v insert", 77ul);
               osi_WrStrLn("                Version, \\\\\\ insert \\\\",
                39ul);
               osi_WrStrLn("                beacon file like: !8959.00N/17959\
.00E&igate mars", 65ul);
               osi_WrStrLn("                beacon file used by udpgate itsel\
f to find out own position", 76ul);
               osi_WrStrLn(" -O             make MH entry for same calls but \
different port", 64ul);
               osi_WrStrLn(" -o <seconds>   ping-pong: time to stop data forw\
arding after last ping", 72ul);
               osi_WrStrLn("                use double time of igate ping int\
ervall", 56ul);
               osi_WrStrLn(" -P <time[:time]> purge unacked (:acked) messages\
 after seconds (-P 86400:300)", 79ul);
               osi_WrStrLn(" -p <password>  login passwort for aprs-is server\
s -p 12345", 60ul);
               osi_WrStrLn("                to hide password in commandline u\
se file mode -p pass.txt", 74ul);
               osi_WrStrLn(" -Q <n>         send netbeacon with qAS if qAI do\
nt pass some servers", 70ul);
               osi_WrStrLn("                0=never, 1=always else every n be\
acons send 1 with qAI", 71ul);
               osi_WrStrLn(" -q <time>      minimum quiet time after rf tx se\
conds (-q 10)", 63ul);
               osi_WrStrLn(" -r <filename>  write a dated 1 day logfile with \
date+time+data lines", 70ul);
               osi_WrStrLn(" -R             same as -M but axudp format",
                44ul);
               osi_WrStrLn(" -s <call>      server call of this server -s MYC\
ALL-10 (-S no callcheck)", 74ul);
               osi_WrStrLn(" -T <seconds>   kill link to server if unack tcp \
bytes are longer in tx queue", 78ul);
               osi_WrStrLn("                avoids delayed trackpoints (defau\
lt 15s, off 0, max 60)", 72ul);
               osi_WrStrLn(" -t <localport> local igate tcp port for in conne\
cts -t 14580", 62ul);
               osi_WrStrLn(" -U <time[:time]> purge unsent(:sent) unack messa\
ges after seconds (-P 900:60)", 79ul);
               osi_WrStrLn(" -v             show frames and analytics on stdo\
ut", 52ul);
               osi_WrStrLn(" -V             Via Path for net to rf frames",
                46ul);
               osi_WrStrLn(" -W <filesize>  limit www server file size in 102\
4byte, (-W 1000)", 66ul);
               osi_WrStrLn(" -w <port>      port of www server -w 14501",
                44ul);
               osi_WrStrLn(" -x <call>      via <call> send messages to rf (-\
x OE0AAA-10) tx off: -x -", 75ul);
               osi_WrStrLn("                default is server call", 39ul);
               osi_WrStrLn(" -Y [num][,num]... bad digis fingerprints to inse\
rt GHOST* in otherwise false", 78ul);
               osi_WrStrLn("                direct heard path", 34ul);
               osi_WrStrLn("udpgate -v -R 127.0.0.1:9200:9201 -s MYCALL-10 -l\
 7:aprs.log -n 10:beacon.txt -t 14580 -g www.server.org:14580#m/30 -p 12345",
                 125ul);
               X2C_ABORT();
            }
            /*
                  ELSIF lasth="u" THEN
                    NextArg(h);
                    IF GetIp(h, rfgateip,
                rfgateport)<0 THEN Err("-u wrong ip:port") END;
            */
            if (lasth=='O') mhperport = 1;
            else if (lasth=='j') {
               osi_NextArg(h, 4096ul);
               i0 = 0UL;
               if (GetSec(h, 4096ul, &i0, &n)>=0L) msgsendtime = n;
               else Err("-j seconds", 11ul);
            }
            else if (lasth=='P') {
               osi_NextArg(h, 4096ul);
               i0 = 0UL;
               if (GetSec(h, 4096ul, &i0, &n)>=0L) purgemsg = n;
               else Err("-P seconds", 11ul);
               if (h[i0]==':') {
                  ++i0;
                  if (GetSec(h, 4096ul, &i0, &n)>=0L) purgeacked = n;
                  else Err("-P seconds:seconds", 19ul);
               }
            }
            else if (lasth=='U') {
               osi_NextArg(h, 4096ul);
               i0 = 0UL;
               if (GetSec(h, 4096ul, &i0, &n)>=0L) purgeunack = n;
               else Err("-U seconds", 11ul);
               if (h[i0]==':') {
                  ++i0;
                  if (GetSec(h, 4096ul, &i0, &n)>=0L) purgeunacksent = n;
                  else Err("-U seconds:seconds", 19ul);
               }
            }
            else if (lasth=='Q') {
               osi_NextArg(h, 4096ul);
               i0 = 0UL;
               if (GetSec(h, 4096ul, &i0, &n)>=0L) qas = n;
               else Err("-Q number", 10ul);
            }
            else if (lasth=='q') {
               osi_NextArg(h, 4096ul);
               i0 = 0UL;
               if (GetSec(h, 4096ul, &i0, &n)>=0L) rfquiet = n;
               else Err("-q seconds", 11ul);
            }
            else if (lasth=='v') verb = 1;
            else if (lasth=='V') {
               osi_NextArg(h, 4096ul);
               aprsstr_Assign(nettorfpath, 81ul, h, 4096ul);
               if (h[0U]==0 || h[0U]=='-' && h[1U]) {
                  Err("-V net to rf via path", 22ul);
               }
            }
            else if (lasth=='k') {
               osi_NextArg(h, 4096ul);
               i0 = 0UL;
               if (GetSec(h, 4096ul, &i0, &n)>=0L) keeptime = n;
               else Err("-k seconds", 11ul);
            }
            else if (lasth=='T') {
               osi_NextArg(h, 4096ul);
               i0 = 0UL;
               if (GetSec(h, 4096ul, &i0, &n)>=0L) qmaxtime = (int32_t)n;
               else Err("-T seconds", 11ul);
               if (qmaxtime>59L) qmaxtime = 59L;
            }
            else if (lasth=='o') {
               osi_NextArg(h, 4096ul);
               i0 = 0UL;
               if (GetSec(h, 4096ul, &i0, &n)>=0L) maxpongtime = n;
               else Err("-o seconds", 11ul);
            }
            else if (lasth=='x') {
               osi_NextArg(h, 4096ul);
               aprsstr_Assign(viacall, 10ul, h, 4096ul);
               if (h[0U]==0 || h[0U]=='-' && h[1U]) Err("-x call", 8ul);
            }
            else if (lasth=='W') {
               osi_NextArg(h, 4096ul);
               i0 = 0UL;
               if (GetSec(h, 4096ul, &i0, &n)>=0L) {
                  wwwsizelimit = (int32_t)(n*1024UL);
               }
               else Err("-W kbytes", 10ul);
            }
            else if (lasth=='Y') {
               osi_NextArg(h, 4096ul);
               i0 = 0UL;
               while (i0<4095UL && h[i0]) {
                  if (GetSec(h, 4096ul, &i0, &n)>=0L) X2C_INCL(ghost,n,257);
                  else Err("-Y <num>", 9ul);
                  if (h[i0]==',') ++i0;
               }
            }
            else if (lasth=='0') datafilter = 1;
            else err = 1;
         }
      }
      else {
         /*h[0]:=0C;*/
         err = 1;
      }
      if (err) break;
   }
   if (err) {
      aprsstr_Append(h, 4096ul, " ? use -h", 10ul);
      Err(h, 4096ul);
   }
   if (servercall[0U]==0) {
      osic_WrLn();
      osi_WrStrLn("udpgate: NO SERVERCALL ?", 25ul);
      osic_WrLn();
   }
   if (wwwbindport[0U] && wwwdir[0]==0) {
      osic_WrLn();
      osi_WrStrLn("udpgate: -w www-port but no -D icon-dir-path ?", 47ul);
      osic_WrLn();
   }
} /* end parms() */


static pUDPSOCK porttosock(uint32_t p)
{
   pUDPSOCK s;
   if (p==0UL) return 0;
   s = udpsocks;
   while (s && p>1UL) {
      s = s->next;
      --p;
   }
   return s;
} /* end porttosock() */


static void Sendudp(const FRAMEBUF s, uint32_t totx, char unlimit)
{
   int32_t len;
   FRAMEBUF raw;
   pUDPSOCK us;
   struct UDPSOCK * anonym;
   /*        udpstat(us, 0, dport, ip); */
   us = porttosock(totx); /* send to which udp modem */
   if (us && (unlimit || (systime-us->lasttxtime)
                *us->maxbytes>us->lasttxbytes)) {
      if (us->rawread) aprsstr_mon2raw(s, 512ul, raw, 512ul, &len);
      else {
         len = (int32_t)(aprsstr_Length(s, 512ul)+1UL);
         memcpy(raw,s,512u);
      }
      if (len>1L) {
         { /* with */
            struct UDPSOCK * anonym = us;
            anonym->lasttxbytes = (uint32_t)len;
            anonym->lasttxtime = systime;
            ++anonym->txframes;
            anonym->txbytes += (uint32_t)len;
         }
         len = udpsend(us->fd, raw, len, us->dport, us->ip);
      }
      else if (verb) osi_WrStrLn("wrong inet to rf frame format", 30ul);
   }
} /* end Sendudp() */


static char getudp(pUDPSOCK usock, FRAMEBUF buf,
                aprsstr_GHOSTSET ghost)
{
   uint32_t fromport;
   uint32_t ipn;
   char crc2;
   /*    len, i       : INTEGER; */
   char crc1;
   int32_t oldi;
   int32_t i0;
   int32_t len;
   uint32_t mlen;
   FRAMEBUF mbuf0;
   uint32_t oldt;
   struct _0 * anonym;
   aprsstr_GHOSTSET tmp;
   int32_t tmp0;
   char tmp1;
   ghost = (uint32_t *)memcpy(tmp,ghost,36u);
   for (;;) {
      len = udpreceive(usock->fd, buf, 512L, &fromport, &ipn);
      if ((len<=2L || len>=512L) || usock->checkip && usock->ip!=ipn) {
         return 0;
      }
      buf[len] = 0;
      udp2[0U] = 0;
      if (usock->rawread) {
         crc1 = buf[len-2L];
         crc2 = buf[len-1L];
         aprsstr_AppCRC(buf, 512ul, len-2L);
         if (crc1!=buf[len-2L] || crc2!=buf[len-1L]) {
            if (verb) osi_WrStrLn(" axudp crc error", 17ul);
            buf[0U] = 0;
         }
         else {
            if (buf[0U]=='\001') {
               aprsstr_extrudp2(buf, 512ul, udp2, 100ul, &len); /* axudp2 */
            }
            if (len>2L) {
               aprsstr_raw2mon(buf, 512ul, mbuf0, 512ul,
                (uint32_t)(len-2L), &mlen, ghost);
               if (mbuf0[0U]==0 && verb) {
                  osi_WrStrLn(" axudp frame decode error", 26ul);
                  tmp0 = len-3L;
                  i0 = 0L;
                  if (i0<=tmp0) for (;; i0++) {
                     osi_WrHex((uint32_t)(uint8_t)buf[i0], 3UL);
                     if (i0==tmp0) break;
                  } /* end for */
                  osic_WrLn();
                  i0 = 0L;
                  for (;;) {
                     if (i0>=len-3L) break;
                     if ((uint32_t)(uint8_t)buf[i0]/2UL<32UL) {
                        osi_WrStr("<", 2ul);
                        osi_WrHex((uint32_t)(uint8_t)buf[i0]/2UL, 1UL);
                        osi_WrStr(">", 2ul);
                     }
                     else {
                        osi_WrStr((char *)(tmp1 = (char)((uint32_t)
                (uint8_t)buf[i0]/2UL),&tmp1), 1u/1u);
                     }
                     if (((uint32_t)(uint8_t)buf[i0]&1)) break;
                     if (i0%7L==6L) osi_WrStr(",", 2ul);
                     ++i0;
                  }
                  osic_WrLn();
               }
               memcpy(buf,mbuf0,512u);
            }
            else buf[0U] = 0;
            len = (int32_t)aprsstr_Length(buf, 512ul); /* for statistic */
         }
      }
      /*-    udpstat(usock, len, fromport, ipn); */
      if (buf[0U]) {
         /* statistic */
         i0 = 0L;
         oldt = systime;
         oldi = 0L;
         for (;;) {
            if (usock->stat[i0].uip==ipn && usock->stat[i0].uport==fromport) {
               oldi = i0;
               i0 = 15L;
            }
            if (i0>=15L) {
               { /* with */
                  struct _0 * anonym = &usock->stat[oldi];
                  anonym->uport = fromport;
                  anonym->uip = ipn;
                  if (anonym->dtime/2UL+anonym->utime/2UL>systime || anonym->utime+600UL<systime)
                 {
                     /* allow clock back step till 1/2 uptime */
                     /* reset statistic */
                     anonym->dtime = systime;
                     /*            usock^.txframes:=0; */
                     /*            usock^.txbytes:=0; */
                     anonym->rxframes = 0UL;
                     anonym->rxbytes = 0UL;
                  }
                  ++anonym->rxframes;
                  anonym->rxbytes += (uint32_t)len;
                  anonym->utime = systime;
                  usock->laststat = (uint32_t)oldi;
               }
               break;
            }
            if (usock->stat[i0].utime<oldt) {
               oldt = usock->stat[i0].utime;
               oldi = i0;
            }
            ++i0;
         }
         return 1;
      }
   }
   return 0;
} /* end getudp() */


static int32_t Gettcp(int32_t fd, FRAMEBUF line, FRAMEBUF buf,
                int32_t * pos)
{
   int32_t j;
   int32_t i0;
   int32_t len;
   int32_t tmp;
   i0 = *pos;
   if (i0>=512L) i0 = 0L;
   /*
     len:=RdBin(fd, line, SIZE(line)-i);
     IF len<=0 THEN RETURN -1 END;
                (* disconnected *)
   */
   /*
     IF len<0 THEN len:=0 END;
   */
   len = readsock(fd, line, 512L-i0);
   if (len<0L) return -1L;
   if (*pos>=512L) {
      /* hunt mode */
      i0 = 0L;
      while (i0<len && line[i0]!='\012') ++i0;
      if (i0>=len) return 0L;
      /* no line start found */
      *pos = 0L;
      ++i0;
      j = 0L;
      while (i0<len) {
         line[j] = line[i0];
         ++j;
         ++i0;
      }
      len = j;
   }
   if (*pos>0L) {
      for (i0 = len-1L; i0>=0L; i0--) {
         line[i0+*pos] = line[i0]; /* move up new */
      } /* end for */
      tmp = *pos-1L;
      i0 = 0L;
      if (i0<=tmp) for (;; i0++) {
         line[i0] = buf[i0]; /* ins buf before */
         if (i0==tmp) break;
      } /* end for */
      len += *pos;
      *pos = 0L;
   }
   i0 = 0L;
   while (i0<len && line[i0]!='\012') ++i0;
   if (i0>=len) {
      /* no line end found */
      while (*pos<len) {
         buf[*pos] = line[*pos]; /* move to buf */
         ++*pos;
      }
      len = 0L;
   }
   else {
      j = i0+1L;
      while (j<len) {
         buf[*pos] = line[j]; /* move rest to buf */
         ++*pos;
         ++j;
      }
      len = i0;
   }
   line[len] = 0;
   return len;
} /* end Gettcp() */


static void showpip(uint32_t ip, uint32_t port, char s[],
                uint32_t s_len)
{
   char h[21];
   aprsstr_IntToStr((int32_t)(ip/16777216UL), 1UL, s, s_len);
   aprsstr_Append(s, s_len, ".", 2ul);
   aprsstr_IntToStr((int32_t)(ip/65536UL&255UL), 1UL, h, 21ul);
   aprsstr_Append(s, s_len, h, 21ul);
   aprsstr_Append(s, s_len, ".", 2ul);
   aprsstr_IntToStr((int32_t)(ip/256UL&255UL), 1UL, h, 21ul);
   aprsstr_Append(s, s_len, h, 21ul);
   aprsstr_Append(s, s_len, ".", 2ul);
   aprsstr_IntToStr((int32_t)(ip&255UL), 1UL, h, 21ul);
   aprsstr_Append(s, s_len, h, 21ul);
   if (port) {
      aprsstr_Append(s, s_len, ":", 2ul);
      aprsstr_IntToStr((int32_t)port, 1UL, h, 21ul);
      aprsstr_Append(s, s_len, h, 21ul);
   }
} /* end showpip() */

/*
PROCEDURE clrstr(VAR s:ARRAY OF CHAR);
VAR i:CARDINAL;
BEGIN
  i:=0;
  WHILE (i<=HIGH(s)) & (s[i]<>0C) DO
    IF s[i]<" " THEN s[i]:="." END;
    INC(i);
  END;
END clrstr;
*/

static void logline(int32_t r, char s[], uint32_t s_len)
{
   char h[512];
   int32_t fd;
   uint32_t i0;
   X2C_PCOPY((void **)&s,s_len);
   if (logframename[0U] && logframes+r>1L) {
      fd = osi_OpenAppend(logframename, 1024ul);
      if (fd<0L) fd = osi_OpenWrite(logframename, 1024ul);
      if (fd>=0L) {
         aprsstr_DateToStr(osic_time(), h, 512ul);
         aprsstr_Append(h, 512ul, " ", 2ul);
         aprsstr_Append(h, 512ul, s, s_len);
         aprsstr_Append(h, 512ul, "\012", 2ul);
         osi_WrBin(fd, (char *)h, 512u/1u, aprsstr_Length(h, 512ul));
         osic_Close(fd);
      }
      else {
         osi_WrStr("cannot write", 13ul);
         osi_WrStr(logframename, 1024ul);
         osic_WrLn();
      }
   }
   if (verb) {
      /* & (r<>-5) */
      i0 = aprsstr_Length(s, s_len);
      if ((i0>=2UL && s[i0-1UL]=='\012') && s[i0-2UL]=='\015') s[i0-2UL] = 0;
      aprsstr_CtrlHex(s, s_len);
      osi_WrStrLn(s, s_len);
   }
   X2C_PFREE(s);
} /* end logline() */


static void writerawlog(const FRAMEBUF b)
{
   char h[512];
   FILENAME fn;
   uint32_t i0;
   uint32_t l;
   int32_t f;
   l = aprsstr_Length(b, 512ul);
   while (l>0UL && (uint8_t)b[l-1UL]<='\015') --l;
   if (l>0UL) {
      aprsstr_DateToStr(osic_time(), h, 512ul);
      h[4U] = h[5U];
      h[5U] = h[6U];
      h[6U] = h[8U];
      h[7U] = h[9U];
      h[8U] = 0;
      h[9U] = h[11U];
      h[10U] = h[12U];
      h[11U] = h[14U];
      h[12U] = h[15U];
      h[13U] = h[17U];
      h[14U] = h[18U];
      h[15U] = ' ';
      aprsstr_Assign(fn, 1024ul, rawlogname, 1024ul);
      aprsstr_Append(fn, 1024ul, h, 512ul);
      h[8U] = ':';
      f = osi_OpenAppend(fn, 1024ul);
      if (f<0L) f = osi_OpenWrite(fn, 1024ul);
      if (f>=0L) {
         if (l>=495UL) l = 495UL;
         i0 = 0UL;
         do {
            h[i0+16UL] = b[i0];
            ++i0;
         } while (i0<l);
         h[i0+16UL] = '\012';
         ++i0;
         osi_WrBin(f, (char *)h, 512u/1u, i0+16UL);
         osic_Close(f);
      }
      else if (verb) {
         osi_WrStr("cannot write ", 14ul);
         osi_WrStrLn(fn, 1024ul);
      }
   }
} /* end writerawlog() */


static void showframe(int32_t r, pTCPSOCK sp, pUDPSOCK su,
                const char buf[], uint32_t buf_len,
                struct aprspos_POSITION pos)
{
   char s[512];
   char h[512];
   struct UDPSOCK * anonym;
   if (sp==0) {
      if (su) {
         strncpy(s,"U:",512u);
         { /* with */
            struct UDPSOCK * anonym = su;
            if (anonym->stat[anonym->laststat]
                .uip!=showip1 || anonym->stat[anonym->laststat]
                .uport!=showport1) {
               showip1 = anonym->stat[anonym->laststat].uip;
               showport1 = anonym->stat[anonym->laststat].uport;
               showpip(showip1, showport1, h, 512ul);
               aprsstr_Append(s, 512ul, h, 512ul);
               aprsstr_Append(s, 512ul, ":", 2ul);
            }
         }
      }
      else strncpy(s," :",512u);
   }
   else {
      s[0U] = sp->service;
      s[1U] = ':';
      s[2U] = 0;
      if (sp->user.call[0U] && !aprsstr_StrCmp(sp->user.call, 10ul, showid,
                10ul)) {
         aprsstr_Append(s, 512ul, sp->user.call, 10ul);
         aprsstr_Append(s, 512ul, ":", 2ul);
         aprsstr_Assign(showid, 10ul, sp->user.call, 10ul);
      }
   }
   /*
   IO.WrFixed(distance(home,pos), 1, 10); IO.WrStr("km"); IO.WrLn;
   */
   switch (r) {
   case 0L:
      if (!aprspos_posvalid(pos) || !aprspos_posvalid(home)) {
         strncpy(h,"--->",512u);
      }
      else {
         aprsstr_IntToStr((int32_t)(uint32_t)
                X2C_TRUNCC(0.5f+aprspos_distance(home, pos),0UL,
                X2C_max_longcard), 3UL, h, 512ul);
         aprsstr_Append(h, 512ul, ">", 2ul);
      }
      break;
   case -1L:
      strncpy(h,"syn:",512u);
      break;
   case -2L:
      strncpy(h,"len:",512u);
      break;
   case -3L:
      strncpy(h,"cal:",512u);
      break;
   case -4L:
      strncpy(h,"msg:",512u);
      break;
   case -5L:
      strncpy(h,"dup:",512u);
      break;
   case -6L:
      strncpy(h,"ung:",512u);
      break;
   default:;
      strncpy(h,"   :",512u);
      break;
   } /* end switch */
   aprsstr_Append(s, 512ul, h, 512ul);
   aprsstr_Append(s, 512ul, buf, buf_len);
   logline(r, s, 512ul);
} /* end showframe() */


static void appkib(char s[], uint32_t s_len, uint32_t h,
                uint32_t l)
{
   char b[41];
   if (h>0UL) {
      aprsstr_CardToStr(l/1024UL+h*4194304UL, 1UL, b, 41ul);
      aprsstr_Append(s, s_len, "kB=", 4ul);
   }
   else {
      aprsstr_CardToStr(l, 1UL, b, 41ul);
      aprsstr_Append(s, s_len, "B=", 3ul);
   }
   aprsstr_Append(s, s_len, b, 41ul);
} /* end appkib() */


static uint32_t bitsec64(uint32_t h, uint32_t l, uint32_t t)
{
   float r;
   r = X2C_DIVR(((float)h*4.294967296E+9f+(float)l)*8.0f,(float)t);
   if (r>2.E+9f) return 2000000000UL;
   return (uint32_t)X2C_TRUNCC(r,0UL,X2C_max_longcard);
} /* end bitsec64() */


static void Statist(pTCPSOCK cp, uint32_t t, char s[],
                uint32_t s_len)
{
   char h[256];
   struct TCPSOCK * anonym;
   { /* with */
      struct TCPSOCK * anonym = cp;
      if (anonym->connt<t) t -= anonym->connt;
      else t = 0UL;
      aprsstr_Assign(s, s_len, " t=", 4ul);
      aprsstr_TimeToStr(t, h, 256ul);
      aprsstr_Append(s, s_len, h, 256ul);
      aprsstr_CardToStr(anonym->txframes, 1UL, h, 256ul);
      aprsstr_Append(s, s_len, " txf=", 6ul);
      aprsstr_Append(s, s_len, h, 256ul);
      aprsstr_CardToStr(anonym->rxframes, 1UL, h, 256ul);
      aprsstr_Append(s, s_len, " rxf=", 6ul);
      aprsstr_Append(s, s_len, h, 256ul);
      aprsstr_Append(s, s_len, " tx", 4ul);
      appkib(s, s_len, anonym->txbytesh, anonym->txbytes);
      aprsstr_Append(s, s_len, " rx", 4ul);
      appkib(s, s_len, anonym->rxbytesh, anonym->rxbytes);
      if (t>0UL) {
         aprsstr_Append(s, s_len, " ", 2ul);
         aprsstr_CardToStr(bitsec64(anonym->txbytesh, anonym->txbytes, t),
                1UL, h, 256ul);
         aprsstr_Append(s, s_len, h, 256ul);
         aprsstr_Append(s, s_len, "tbit/s ", 8ul);
         aprsstr_CardToStr(bitsec64(anonym->rxbytesh, anonym->rxbytes, t),
                1UL, h, 256ul);
         aprsstr_Append(s, s_len, h, 256ul);
         aprsstr_Append(s, s_len, "rbit/s", 7ul);
      }
   }
} /* end Statist() */


static void showlogout(pTCPSOCK sp)
{
   char t[256];
   char h[256];
   struct TCPSOCK * anonym;
   { /* with */
      struct TCPSOCK * anonym = sp;
      if (anonym->slowlink) strncpy(h,"T:slowlink closed ",256u);
      else strncpy(h,"T:connection closed ",256u);
      aprsstr_Append(h, 256ul, anonym->user.call, 10ul);
      aprsstr_Append(h, 256ul, " ", 2ul);
      aprsstr_Append(h, 256ul, anonym->ipnum, 64ul);
      if (anonym->connt) {
         Statist(sp, systime, t, 256ul);
         /*
               ti:=time();
               IF ti<connt THEN ti:=0 ELSE DEC(ti, connt) END;
               Append(h," conntime ");
               TimeToStr(ti, t); 
         */
         aprsstr_Append(h, 256ul, t, 256ul);
      }
      aprsstr_Append(h, 256ul, "\012", 2ul);
   }
   logline(1L, h, 256ul);
} /* end showlogout() */


static char vias(char dat[], uint32_t dat_len, pTCPSOCK to)
{
   uint32_t j;
   uint32_t i0;
   uint32_t b;
   uint32_t a;
   struct FILTERS * anonym;
   if (to->filters.viacalls[0U][0U]==0 || dat[0UL]==0) return 0;
   b = 0UL;
   do {
      a = b+1UL;
      do {
         ++b;
         if (b>dat_len-1 || dat[b]==0) return 0;
      } while (!(dat[b]==',' || dat[b]==':'));
      /* not normal data */
      if (a>1UL) {
         j = 0UL;
         { /* with */
            struct FILTERS * anonym = &to->filters;
            while (j<=7UL && anonym->viacalls[j][0U]) {
               i0 = 0UL;
               for (;;) {
                  if (a+i0>=b || dat[a+i0]=='*') {
                     /* end of word 1 ignore h-bit "*" */
                     if ((i0>9UL || anonym->viacalls[j][i0]==0)
                || anonym->viacalls[j][i0]
                =='*' && (i0+1UL>9UL || anonym->viacalls[j][i0+1UL]==0)) {
                        return 1;
                     }
                     else break;
                  }
                  if (i0>9UL || anonym->viacalls[j][i0]==0) {
                     break; /* end of word 2 */
                  }
                  if (anonym->viacalls[j][i0]
                =='*' && (i0+1UL>9UL || anonym->viacalls[j][i0+1UL]==0)) {
                     return 1;
                  }
                  /* word 2 "*" at end */
                  if (anonym->viacalls[j][i0]!='*' && anonym->viacalls[j][i0]
                !=dat[a+i0]) break;
                  ++i0;
               }
               ++j; /* next call in table */
            }
         }
      }
   } while (dat[b]!=':');
   return 0;
} /* end vias() */


static char entrypoint(char dat[], uint32_t dat_len,
                pTCPSOCK to)
{
   uint32_t j;
   uint32_t i0;
   uint32_t b;
   uint32_t a;
   struct FILTERS * anonym;
   if (to->filters.entrycalls[0U][0U]==0) return 0;
   i0 = 0UL;
   for (;;) {
      if ((i0>(dat_len-1)-5UL || dat[i0]==0) || dat[i0]==':') return 0;
      /* no qA */
      if (((dat[i0]==',' && dat[i0+1UL]=='q') && dat[i0+2UL]=='A')
                && dat[i0+4UL]==',') {
         /* ,qA., */
         a = i0+5UL;
         b = a;
         while (((b<dat_len-1 && dat[b]) && dat[b]!=':') && dat[b]!=',') ++b;
         break;
      }
      ++i0;
   }
   j = 0UL;
   { /* with */
      struct FILTERS * anonym = &to->filters;
      while (j<=7UL && anonym->entrycalls[j][0U]) {
         i0 = 0UL;
         for (;;) {
            if (a+i0>=b) {
               /* end of word 1 */
               if ((i0>9UL || anonym->entrycalls[j][i0]==0)
                || anonym->entrycalls[j][i0]
                =='*' && (i0+1UL>9UL || anonym->entrycalls[j][i0+1UL]==0)) {
                  return 1;
               }
               else break;
            }
            if (i0>9UL || anonym->entrycalls[j][i0]==0) break;
            if (anonym->entrycalls[j][i0]
                =='*' && (i0+1UL>9UL || anonym->entrycalls[j][i0+1UL]==0)) {
               return 1;
            }
            /* word 2 "*" at end */
            if (anonym->entrycalls[j][i0]!='*' && anonym->entrycalls[j][i0]
                !=dat[a+i0]) break;
            ++i0;
         }
         /*
                 WHILE (a+i<b) & ((entrycalls[j][i]="*")
                OR (entrycalls[j][i]=dat[a+i])) DO
                   INC(i);
                   IF (a+i>=b) & ((i>HIGH(entrycalls[0]))
                OR (entrycalls[j][i]=0C)) THEN RETURN TRUE END;
         
         */
         ++j;
      }
   }
   return 0;
} /* end entrypoint() */


static char prefix(char dat[], uint32_t dat_len, pTCPSOCK to)
{
   uint32_t j;
   uint32_t i0;
   struct FILTERS * anonym;
   if (to->filters.prefixes[0U][0U]==0) return 0;
   j = 0UL;
   { /* with */
      struct FILTERS * anonym = &to->filters;
      while (j<=7UL && anonym->prefixes[j][0U]) {
         i0 = 0UL;
         while (dat[i0]!='>' && anonym->prefixes[j][i0]==dat[i0]) {
            ++i0;
            if (i0>9UL || anonym->prefixes[j][i0]==0) return 1;
         }
         ++j;
      }
   }
   return 0;
} /* end prefix() */


static char destcallfilt(char dat[], uint32_t dat_len,
                pTCPSOCK to)
{
   uint32_t b;
   uint32_t a;
   uint32_t j;
   uint32_t i0;
   struct FILTERS * anonym;
   if (to->filters.destcalls[0U][0U]==0) return 0;
   a = 0UL;
   while (dat[a]!='>') {
      /* begin of dest call ">" */
      if (a>=dat_len-1 || dat[a]==0) return 0;
      /* not normal data */
      ++a;
   }
   ++a;
   b = a;
   for (;;) {
      if (b>dat_len-1 || dat[b]==0) return 0;
      /* not normal data */
      if (dat[b]==',' || dat[b]==':') break;
      ++b;
   }
   b -= a; /* len of dest call */
   j = 0UL;
   { /* with */
      struct FILTERS * anonym = &to->filters;
      while (j<=7UL && anonym->destcalls[j][0U]) {
         i0 = 0UL;
         while (i0<b && (anonym->destcalls[j][i0]
                =='*' || anonym->destcalls[j][i0]==dat[i0+a])) {
            ++i0;
            if (i0>9UL || anonym->destcalls[j][i0]==0) return 1;
         }
         ++j;
      }
   }
   return 0;
} /* end destcallfilt() */


static char typ(struct POSCALL * posc, char dat[],
                uint32_t dat_len, pTCPSOCK to, char * t)
{
   uint32_t ii;
   uint32_t j;
   uint32_t i0;
   char c;
   if (to->filters.typs[0U]==0) return 0;
   i0 = 0UL;
   j = 0UL;
   c = 0;
   for (;;) {
      if (i0>(dat_len-1)-2UL || dat[i0]==0) return 0;
      /* no typ */
      if (dat[i0]==':') {
         /* outer typ */
         c = dat[i0+1UL];
         if (c!='}') {
            i0 += 2UL; /* inner typchar found*/
            break;
         }
         if (aprsstr_InStr(to->filters.typs, 13ul, "3", 2ul)>=0L) return 1;
         /* 3rd party frame */
         j = i0+2UL; /* start of inner frame */
      }
      ++i0;
   }
   if (c==':') {
      ii = i0;
      while (ii<dat_len-1 && dat[j]==dat[ii]) {
         ++ii; /* msg to itself */
         ++j;
      }
      if (dat[j]=='>') {
         c = 'T'; /* gets type telemetry */
         *t = 'T';
      }
   }
   if (aprspos_posvalid(posc->pos) && aprsstr_InStr(to->filters.typs, 13ul, "\
p", 2ul)>=0L) return 1;
   /* position frame */
   if (c==';' && aprsstr_InStr(to->filters.typs, 13ul, "o", 2ul)>=0L) {
      return 1;
   }
   /* object */
   if (c==')') return aprsstr_InStr(to->filters.typs, 13ul, "i", 2ul)>=0L;
   /* item */
   if (c==':') {
      if (dat[i0+10UL]=='?') {
         return aprsstr_InStr(to->filters.typs, 13ul, "q", 2ul)>=0L;
      }
      else if ((((i0<(dat_len-1)-6UL && dat[i0]=='N') && dat[i0+1UL]=='W')
                && dat[i0+2UL]=='S') && dat[i0+3UL]=='-') {
         /* query */
         return aprsstr_InStr(to->filters.typs, 13ul, "n", 2ul)>=0L;
      }
      else {
         /* NWS bulletin */
         return aprsstr_InStr(to->filters.typs, 13ul, "m", 2ul)>=0L;
      }
   }
   /* message */
   if (c=='>') return aprsstr_InStr(to->filters.typs, 13ul, "s", 2ul)>=0L;
   /* status */
   if (c=='T') return aprsstr_InStr(to->filters.typs, 13ul, "t", 2ul)>=0L;
   /* telemetry */
   if (c=='{') return aprsstr_InStr(to->filters.typs, 13ul, "u", 2ul)>=0L;
   /* user defined */
   if (aprsstr_InStr(to->filters.typs, 13ul, "w", 2ul)>=0L) {
      /* test if wx */
      j = 0UL;
      if (c=='_') return 1;
      /* positionless wx */
      if (c=='!' || c=='=') {
         if ((uint8_t)dat[i0]>='0' && (uint8_t)dat[i0]<='9') j = i0+18UL;
         else j = i0+9UL;
      }
      else if (c=='/' || c=='@') {
         if ((uint8_t)dat[i0+7UL]>='0' && (uint8_t)dat[i0+7UL]<='9') {
            j = i0+25UL;
         }
         else j = i0+16UL;
      }
      else if (c==';') j = i0+35UL;
      /*WrStr("<<<"); WrStr(dat[j]);WrStrLn(">>>");  */
      if (((j>0UL && j<dat_len-1) && dat[j]=='_')
                && aprspos_posvalid(posc->pos)) return 1;
   }
   return 0;
} /* end typ() */


static char Filter(pTCPSOCK to, struct POSCALL posc,
                const char dat[], uint32_t dat_len)
{
   char pass;
   pass = 0;
   if (vias(dat, dat_len, to)) {
      if (to->filters.notvia) return 0;
      pass = 1; /* exclusion overloads */
   }
   if (entrypoint(dat, dat_len, to)) {
      if (to->filters.notentry) return 0;
      pass = 1;
   }
   if (destcallfilt(dat, dat_len, to)) {
      if (to->filters.notdestcall) return 0;
      pass = 1;
   }
   if (prefix(dat, dat_len, to)) {
      if (to->filters.notprefix) return 0;
      pass = 1;
   }
   if (typ(&posc, dat, dat_len, to, &posc.typ0)) {
      if (to->filters.nottyps) return 0;
      pass = 1;
   }
   if (pass || posc.typ0==':') return 1;
   /* pass messages and ack */
   if (to->filters.typ0=='m') {
      return (aprspos_posvalid(to->user.pos) && aprspos_posvalid(posc.pos))
                && aprspos_distance(to->user.pos,
                posc.pos)<=to->filters.radius;
   }
   if (to->filters.typ0=='r') {
      /*
      IO.WrStr("l,b,l,b,d,r:"); IO.WrFixed(to^.filters.base.lat/RAD, 4,10);
                IO.WrFixed(to^.filters.base.long/RAD, 4,10);  
      IO.WrFixed(posc.pos.lat/RAD, 4,10);
                IO.WrFixed(posc.pos.long/RAD, 4,10);
      IO.WrFixed(distance(to^.filters.base, posc.pos), 4,10);
                IO.WrFixed(to^.filters.radius, 4,10);
      IO.WrLn;
      */
      return aprspos_posvalid(posc.pos) && aprspos_distance(to->filters.base,
                 posc.pos)<=to->filters.radius;
   }
   if (to->filters.typ0=='a') {
      /*
      IO.WrStr("l,b,l,b,l,b,d,r:"); 
      IO.WrFixed(to^.filters.base.lat/RAD, 4,10);
                IO.WrFixed(to^.filters.base.long/RAD, 4,10);  
      IO.WrFixed(to^.filters.edge.lat/RAD, 4,10);
                IO.WrFixed(to^.filters.edge.long/RAD, 4,10);
      IO.WrFixed(posc.pos.lat/RAD, 4,10);
                IO.WrFixed(posc.pos.long/RAD, 4,10);
      IO.WrLn;
      */
      return (((aprspos_posvalid(posc.pos)
                && to->filters.base.lat>=posc.pos.lat)
                && to->filters.base.long0<=posc.pos.long0)
                && to->filters.edge.lat<=posc.pos.lat)
                && to->filters.edge.long0>=posc.pos.long0;
   }
   return !datafilter;
} /* end Filter() */


static void sendtcpbuf(pTCPSOCK to)
{
   int32_t i0;
   int32_t res0;
   struct TCPSOCK * anonym;
   int32_t tmp;
   { /* with */
      struct TCPSOCK * anonym = to;
      res0 = sendsock(anonym->fd, anonym->tbuf, anonym->tlen);
      if (res0>0L) {
         tmp = anonym->tlen-1L;
         i0 = res0;
         if (i0<=tmp) for (;; i0++) {
            anonym->tbuf[i0-res0] = anonym->tbuf[i0];
            if (i0==tmp) break;
         } /* end for */
         anonym->tlen -= res0;
      }
   }
} /* end sendtcpbuf() */


static void Sendtcp(pTCPSOCK to, const FRAMEBUF buf)
{
   int32_t i0;
   int32_t len;
   struct TCPSOCK * anonym;
   int32_t tmp;
   len = (int32_t)aprsstr_Length(buf, 512ul);
   { /* with */
      struct TCPSOCK * anonym = to;
      if (anonym->tlen+len>=1024L) sendtcpbuf(to);
      if (anonym->tlen+len<1024L) {
         tmp = len-1L;
         i0 = 0L;
         if (i0<=tmp) for (;; i0++) {
            anonym->tbuf[anonym->tlen] = buf[i0];
            ++anonym->tlen;
            if (i0==tmp) break;
         } /* end for */
         ++anonym->txframes;
         anonym->txbytes += (uint32_t)len; /* 32 bit overflow */
         if (anonym->txbytes<(uint32_t)len) ++anonym->txbytesh;
      }
      else {
         ++anonym->losttxframes;
         if (verb) osi_WrStrLn("tx buf overflow", 16ul);
      }
   }
} /* end Sendtcp() */


static char filt(int32_t outer, char b[], uint32_t b_len,
                const char s[], uint32_t s_len)
{
   int32_t n;
   n = aprsstr_InStr(b, b_len, s, s_len);
   return n>=0L && n<outer;
} /* end filt() */


static void NetToRf(const char b[], uint32_t b_len, uint32_t rfport)
/* send as 3rd party frame to rf */
{
   FRAMEBUF tb;
   uint32_t viaused;
   uint32_t viaend;
   uint32_t len;
   uint32_t i0;
   int32_t ii;
   int32_t outer;
   outer = aprsstr_InStr(b, b_len, ":", 2ul);
                /* test if gate to rf allowed */
   if (outer<0L || filt(outer, b, b_len, "qAX", 4ul)) return;
   viaend = (uint32_t)outer;
   while (outer<(int32_t)(b_len-1) && (b[outer]!=':' || b[outer+1L]=='}')) {
      ++outer;
   }
   if ((filt(outer, b, b_len, "TCPXX", 6ul) || filt(outer, b, b_len,
                "NOGATE", 7ul)) || filt(outer, b, b_len, "RFONLY", 7ul)) {
      return;
   }
   aprsstr_Assign(tb, 512ul, viacall, 10ul);
   aprsstr_Append(tb, 512ul, ">APNL51", 8ul);
   if (nettorfpath[0U]) {
      aprsstr_Append(tb, 512ul, ",", 2ul);
      aprsstr_Append(tb, 512ul, nettorfpath, 81ul);
   }
   aprsstr_Append(tb, 512ul, ":}", 3ul);
   viaused = 0UL;
   while (viaused<viaend && b[viaused]!=',') ++viaused;
   ii = aprsstr_InStr(b, b_len, ",TCPIP", 7ul);
   if (ii<0L || ii>(int32_t)viaend) {
      /* remove all via if via TCPIP */
      i0 = viaused;
      while (i0<viaend && b[i0]!='*') ++i0;
      if (i0<viaend) viaused = i0;
   }
   len = aprsstr_Length(tb, 512ul);
   i0 = 0UL;
   /*  WHILE (b[i]<>",") & (i<viaend) & (len<=HIGH(tb)) DO           (* original head remove via*) */
   while (i0<viaused && len<=511UL) {
      /* original head with used via*/
      tb[len] = b[i0];
      ++i0;
      ++len;
   }
   if (len<=511UL) tb[len] = 0;
   aprsstr_Append(tb, 512ul, ",TCPIP,", 8ul);
   aprsstr_Append(tb, 512ul, viacall, 10ul);
   aprsstr_Append(tb, 512ul, "*", 2ul);
   len = aprsstr_Length(tb, 512ul);
   i0 = viaend;
   while ((i0<=b_len-1 && b[i0]) && len<=511UL) {
      /* original data part */
      tb[len] = b[i0];
      ++i0;
      ++len;
   }
   if (len<=511UL) tb[len] = 0;
   i0 = 0UL;
   while (((i0<511UL && tb[i0]) && tb[i0]!='\015') && tb[i0]!='\012') ++i0;
   tb[i0] = 0;
   Sendudp(tb, rfport, 0);
} /* end NetToRf() */


static void Sendall(const FRAMEBUF buf, int32_t fromfd,
                const struct POSCALL posc)
{
   pTCPSOCK t;
   pUDPSOCK u;
   uint32_t uport;
   /*
       len:INTEGER;
   */
   if (buf[0U]) {
      t = tcpsocks;
      while (t) {
         if (((t->fd!=fromfd && t->service!='W') && t->connt>0UL)
                && (t->service!='S' || Filter(t, posc, buf, 512ul))) {
            Sendtcp(t, buf);
         }
         t = t->next;
      }
      if ((fromfd>0L && aprspos_posvalid(home)) && aprspos_posvalid(posc.pos)
                ) {
         u = udpsocks;
         uport = 1UL;
         while (u) {
            if (u->torfradius>0.0f && aprspos_distance(home,
                posc.pos)<=u->torfradius) NetToRf(buf, 512ul, uport);
            ++uport;
            u = u->next;
         }
      }
      /*
          IF rfgateport>0 THEN 
            len:=Length(buf);
            len:=udp.udpsend(udpsock.fd, buf, len, rfgateport, rfgateip);
          END;
      */
      if (rawlogname[0U]) writerawlog(buf);
   }
} /* end Sendall() */


static char cmpfrom(const char a[], uint32_t a_len,
                uint32_t from, const char b[], uint32_t b_len)
{
   uint32_t i0;
   i0 = 0UL;
   while (from<=a_len-1 && b[i0]) {
      if (a[from]!=b[i0]) return 0;
      ++i0;
      ++from;
   }
   return 1;
} /* end cmpfrom() */

#define udpgate4_MSYM "\\"


static void beaconmacros(char s[], uint32_t s_len)
{
   uint32_t i0;
   int32_t j;
   int32_t len;
   char ns[256];
   char ds[256];
   char fn[1024];
   int32_t f;
   i0 = 0UL;
   ns[0U] = 0;
   while (i0<s_len-1 && s[i0]) {
      if (s[i0]=='\\' && s[i0+1UL]=='\\') {
         i0 += 2UL;
         if (s[i0]=='z') {
            /* insert day, hour, min */
            aprsstr_DateToStr(osic_time(), ds, 256ul);
            ds[0U] = ds[8U];
            ds[1U] = ds[9U];
            ds[2U] = ds[11U];
            ds[3U] = ds[12U];
            ds[4U] = ds[14U];
            ds[5U] = ds[15U];
            ds[6U] = 0;
            aprsstr_Append(ns, 256ul, ds, 256ul);
         }
         else if (s[i0]=='h') {
            /* insert hour, min, s */
            aprsstr_DateToStr(osic_time(), ds, 256ul);
            ds[0U] = ds[11U];
            ds[1U] = ds[12U];
            ds[2U] = ds[14U];
            ds[3U] = ds[15U];
            ds[4U] = ds[17U];
            ds[5U] = ds[18U];
            ds[6U] = 0;
            aprsstr_Append(ns, 256ul, ds, 256ul);
         }
         else if (s[i0]=='v') {
            /* insert version */
            aprsstr_Append(ns, 256ul, "udpgate(c) 0.63", 16ul);
         }
         else if (s[i0]==':') {
            /* insert file */
            fn[0U] = 0;
            ++i0;
            while ((i0<s_len-1 && s[i0]) && s[i0]!=':') {
               aprsstr_Append(fn, 1024ul, (char *) &s[i0], 1u/1u);
               ++i0;
            }
            f = osi_OpenRead(fn, 1024ul);
            if (f>=0L) {
               len = osi_RdBin(f, (char *)ds, 256u/1u, 255UL);
               osic_Close(f);
               j = 0L;
               while (((j<len && ds[j]!='\015') && ds[j]!='\012') && ds[j]) {
                  aprsstr_Append(ns, 256ul, (char *) &ds[j], 1u/1u);
                  ++j;
               }
            }
            else {
               if (verb) {
                  osic_WrLn();
                  osi_WrStrLn("beacon macro file not readable ", 32ul);
               }
               s[0UL] = 0;
               return;
            }
         }
         else if (s[i0]=='\\') aprsstr_Append(ns, 256ul, "\\\\", 3ul);
         else {
            if (verb) {
               osic_WrLn();
               osi_WrStrLn("bad beacon macro ", 18ul);
            }
            s[0UL] = 0;
            return;
         }
      }
      else aprsstr_Append(ns, 256ul, (char *) &s[i0], 1u/1u);
      ++i0;
   }
   aprsstr_Assign(s, s_len, ns, 256ul);
} /* end beaconmacros() */


static void AppQ(char h[], uint32_t h_len, const char q[],
                uint32_t q_len, char appcall)
{
   aprsstr_Append(h, h_len, ">APNL51,", 9ul);
   aprsstr_Append(h, h_len, q, q_len);
   if (appcall) aprsstr_Append(h, h_len, servercall, 10ul);
   aprsstr_Append(h, h_len, ":", 2ul);
} /* end AppQ() */


static void Netbeacon(char h[], uint32_t h_len, char qai,
                char withpath)
{
   int32_t valt0;
   int32_t i0;
   int32_t f;
   FRAMEBUF h1;
   uint32_t j;
   uint32_t vspeed0;
   uint32_t vcourse0;
   char vsymt0;
   char vsym0;
   char postyp;
   h[0UL] = 0;
   if (servercall[0U] && netbeaconfn[0U]) {
      f = osi_OpenRead(netbeaconfn, 1024ul);
      if (f>=0L) {
         i0 = osi_RdBin(f, (char *)h1, 512u/1u, 511UL);
         if (i0>=0L) {
            h1[i0] = 0;
            i0 = 0L;
            while ((uint8_t)h1[i0]>=' ') ++i0;
            h1[i0] = 0;
            beaconmacros(h1, 512ul);
            if (withpath) {
               aprsstr_Assign(h, h_len, servercall, 10ul);
               if (qai) AppQ(h, h_len, "TCPIP*,qAI,", 12ul, 1);
               else AppQ(h, h_len, "TCPIP*", 7ul, 0);
               aprsstr_Append(h, h_len, h1, 512ul);
               aprsstr_Append(h, h_len, "\015\012", 3ul);
            }
            else aprsstr_Assign(h, h_len, h1, 512ul);
         }
         osic_Close(f);
         j = 0UL;
         while (j<(h_len-1)-1UL && h[j]!=':') ++j;
         aprspos_GetPos(&home, &vspeed0, &vcourse0, &valt0, &vsym0, &vsymt0,
                h, h_len, 0UL, j+1UL, h1, 512ul, &postyp);
                /* find server position */
         if (verb && !aprspos_posvalid(home)) {
            osi_WrStrLn("netbeacon has no valid position", 32ul);
         }
      }
      else if (verb) osi_WrStrLn("netbeacon file not found", 25ul);
   }
} /* end Netbeacon() */


static void Timebeacon(pTCPSOCK cp)
{
   FRAMEBUF h;
   char qai;
   if (cp->service!='W' && Watchclock(&cp->beacont, netbeaconintervall)) {
      qai = 1;
      if (cp->service=='G') {
         if (qas==1UL || qas>qasc+1UL) {
            qai = 0;
            ++qasc;
         }
         else qasc = 0UL;
      }
      Netbeacon(h, 512ul, qai, 1);
      if (h[0U]) Sendtcp(cp, h);
   }
/*
PROCEDURE inetrf(buf-:ARRAY OF CHAR); 
CONST KW="RFONLY,";
VAR p,i:CARDINAL;
    c:CHAR;
    mon:FRAMEBUF;
BEGIN
(* call>RFONLY,igatecall:}txdata *)
  p:=0;
  LOOP
    IF buf[p]=0C THEN RETURN END;
    IF buf[p]=">" THEN EXIT END;
    INC(p);
  END;
  INC(p);
  i:=0;
  LOOP
    IF buf[p]=0C THEN RETURN END;
    IF KW[i]=0C THEN EXIT END;
    IF buf[p]<>KW[i] THEN RETURN END;
    INC(p);
    INC(i);
  END;
  i:=0;
  LOOP
    IF buf[p]=0C THEN RETURN END;
    IF (i>HIGH(servercall)) OR (servercall[i]=0C) THEN EXIT END;
    IF buf[p]<>servercall[i] THEN RETURN END;
    INC(p);
    INC(i);
  END;
  IF buf[p]<>":" THEN RETURN END;
  INC(p);
  IF buf[p]<>cTHIRDPARTY THEN RETURN END;
  INC(p);
  i:=0;  
  LOOP
    c:=buf[p];
    IF (i>=HIGH(mon)) OR (c=CR) THEN c:=0C END;
    mon[i]:=c; 
    IF c=0C THEN EXIT END;
    INC(p); 
    INC(i);
  END; 
  Sendudp(mon);
END inetrf;
*/
} /* end Timebeacon() */


static void Showmsg(pMESSAGE m)
{
   struct MESSAGE * anonym;
   { /* with */
      struct MESSAGE * anonym = m;
      osic_WrINT32(anonym->retryc, 3UL);
      osic_WrINT32((uint32_t)anonym->acked, 1UL);
      if (anonym->txtime) osic_WrINT32(systime-anonym->txtime, 5UL);
      else osi_WrStr(" notx", 6ul);
      osi_WrStr(":", 2ul);
      osi_WrStr(anonym->from, 10ul);
      osi_WrStr(">", 2ul);
      osi_WrStr(anonym->to, 10ul);
      osi_WrStr("|", 2ul);
      osi_WrStr(anonym->ack, 5ul);
      osi_WrStr(anonym->replyack, 2ul);
      osi_WrStr("|", 2ul);
      osi_WrStr(anonym->text, 68ul);
      osi_WrStrLn("|", 2ul);
   }
} /* end Showmsg() */


static void cpcall(char s[], uint32_t s_len, const char h[],
                uint32_t h_len, uint32_t pr)
{
   char c;
   uint32_t i0;
   i0 = 0UL;
   for (;;) {
      c = h[pr];
      if ((((((i0>=s_len-1 || c==0) || c=='>') || c=='*') || c==':')
                || c==',') || c==' ') break;
      s[i0] = c;
      ++i0;
      ++pr;
   }
   s[i0] = 0;
} /* end cpcall() */


static void SendMsg(pMESSAGE mp, uint32_t torf)
{
   FRAMEBUF s;
   uint32_t i0;
   MONCALL h;
   /*
       tcp:BOOLEAN;
   */
   struct POSCALL poscall0;
   uint8_t srcpath;
   pUDPSOCK uds;
   struct MESSAGE * anonym;
   /* rf: OE2XZR-10>RFONLY,OE2XZR-10:}
                OE0AAA>TCPIP,OE2XZR-10::OE2WAO-12:hallo{123 */
   /* ip: OE0AAA>TCPIP,OE2XZR-10::OE2WAO-12:hallo{123 */
   if (verb) {
      if (viacall[0U]==0) osi_WrStr("<no via call, to msg tx>", 25ul);
      else if (torf) osi_WrStr("<send rf>", 10ul);
      else osi_WrStr("<send net>", 11ul);
      Showmsg(mp);
   }
   if (mp->ackackt || viacall[0U]==0) return;
   /*
     tcp:=udpsock.fd<0;
     s[0]:=0C;
     IF (torf<>0) & tcp THEN
       Append(s, viacall);
       Append(s, ">RFONLY,");
       Append(s, servercall);
       Append(s, ":}");
     END;
     IF mp^.acked THEN Append(s, mp^.to) ELSE Append(s, mp^.from) END;
   */
   if (mp->acked) {
      aprsstr_Assign(s, 512ul, mp->to, 10ul);
      srcpath = mp->acksrc;
   }
   else {
      aprsstr_Assign(s, 512ul, mp->from, 10ul);
      srcpath = mp->src;
   }
   if (torf) {
      aprsstr_Append(s, 512ul, ">", 2ul);
      if (srcpath==udpgate4_NET) aprsstr_Append(s, 512ul, netdestcall, 10ul);
      else aprsstr_Append(s, 512ul, rfdestcall, 10ul);
      aprsstr_Append(s, 512ul, ",", 2ul);
      aprsstr_Append(s, 512ul, viacall, 10ul);
      aprsstr_Append(s, 512ul, "*::", 4ul);
   }
   else {
      if (mp->queryrep) AppQ(s, 512ul, "qAS,", 5ul, 1);
      else AppQ(s, 512ul, "NOGATE*,qAS,", 13ul, 1);
      aprsstr_Append(s, 512ul, ":", 2ul);
   }
   if (mp->acked) memcpy(h,mp->from,10u);
   else memcpy(h,mp->to,10u);
   for (i0 = aprsstr_Length(h, 10ul); i0<=8UL; i0++) {
      aprsstr_Append(h, 10ul, " ", 2ul);
   } /* end for */
   aprsstr_Append(s, 512ul, h, 10ul);
   if (mp->acked) {
      if (mp->reject) aprsstr_Append(s, 512ul, ":rej", 5ul);
      else aprsstr_Append(s, 512ul, ":ack", 5ul);
   }
   else {
      aprsstr_Append(s, 512ul, ":", 2ul);
      { /* with */
         struct MESSAGE * anonym = mp;
         i0 = 0UL;
         while (i0<=67UL && anonym->text[i0]) {
            /* blank not allowed chars */
            if (((anonym->text[i0]=='|' || anonym->text[i0]=='~')
                || anonym->text[i0]=='{') || (uint8_t)anonym->text[i0]<' ')
                 anonym->text[i0] = ' ';
            ++i0;
         }
         aprsstr_Append(s, 512ul, anonym->text, 68ul);
         if (anonym->ack[0U]) {
            aprsstr_Append(s, 512ul, "{", 2ul);
         }
      }
   }
   aprsstr_Append(s, 512ul, mp->ack, 5ul);
   aprsstr_Append(s, 512ul, mp->replyack, 2ul);
   ++mp->retryc;
   poscall0.typ0 = ':';
   if (torf) {
      /*NOT tcp &*/
      Sendudp(s, torf, 1);
   }
   else {
      aprsstr_Append(s, 512ul, "\015\012", 3ul);
      Sendall(s, 0L, poscall0);
   }
   mp->txport = torf;
   if (verb || logframes>1L) {
      uds = porttosock(torf);
      showframe(-4L, 0, uds, s, 512ul, poscall0.pos);
   }
   if (mp->acked) {
      if (torf) ++mp->ack2rf;
      else ++mp->ack2net;
   }
   else if (torf) ++mp->msg2rf;
   else ++mp->msg2net;
   mp->txtime = systime;
} /* end SendMsg() */


static uint32_t FindUserHeard(pHEARD * ph, const MONCALL c,
                uint32_t * rfport)
/* never heard or too long = 0 else time+1 */
{
   while (*ph && (*ph)->time0+heardtime>systime) {
      if (aprsstr_StrCmp((*ph)->call, 10ul, c, 10ul)) {
         if ((*ph)->time0<=systime) {
            *rfport = (*ph)->fromrx;
            return (1UL+systime)-(*ph)->time0;
         }
         else return 0UL;
      }
      *ph = (*ph)->next;
   }
   return 0UL;
} /* end FindUserHeard() */


static uint32_t FindHeard(pHEARD ph, const MONCALL c, uint32_t * rfport)
/* never heard or too long = 0 else time+1 */
{
   return FindUserHeard(&ph, c, rfport);
} /* end FindHeard() */


static char Heard(const char b[], uint32_t b_len, MONCALL from,
                char * trust)
{
   uint32_t len;
   uint32_t i0;
   uint32_t p;
   /* from>to,path,gate:...*/
   p = 0UL;
   while (p<9UL && b[p]!='>') {
      from[p] = b[p]; /* get user call */
      ++p;
   }
   from[p] = 0;
   i0 = aprsstr_Length(b, b_len);
   len = 0UL;
   while (len<i0 && b[len]!=':') ++len;
   *trust = b[len+1UL]!=':' && b[len+1UL]!='}';
                /* not from physical sender */
   /*
     IF NOT udp THEN                                               (* frame is from aprsis *)
       lp:=Length(rfpath);
       IF (lp=0) OR (lp>len) THEN RETURN END;
                (* rfpath not fitting *)
   
       DEC(len, lp);                                               (* cut rfpath *)
       FOR i:=0 TO lp-1 DO
         IF b[len+i]<>rfpath[i] THEN RETURN END;
                (* wrong rfpath *)
       END;
     END;
   */
   while (p<len && b[p]!=',') ++p;
   if (b[p]==',') {
      /* get first via */
      ++p;
      i0 = p;
      while (p<len && b[p]!=',') ++p;
      if (b[p-1UL]=='*') return 0;
      /* is repeated */
      if (((uint8_t)b[p-1UL]>='0' && (uint8_t)b[p-1UL]<='9')
                && b[p-1UL]!=b[p-3UL]) return 0;
      /* WIDEn-x with n<>x */
      /*
          ELSIF ((p-i<=3) OR (b[p-2]<>"-") OR (b[p-1]<>b[p-3])
                OR (b[p-1]<"1") OR (b[p-1]>"7"))
            & ((p-i<>4) OR (b[i]<>"E") OR (b[i+1]<>"C") OR (b[i+2]<>"H")
                OR (b[i+3]<>"O"))
            & ((p-i<>4) OR (b[i]<>"G") OR (b[i+1]<>"A") OR (b[i+2]<>"T")
                OR (b[i+3]<>"E"))
            & ((p-i<>5) OR (b[i]<>"R") OR (b[i+1]<>"E") OR (b[i+2]<>"L")
                OR (b[i+3]<>"A") OR (b[i+4]<>"Y"))
          THEN RETURN FALSE END; (* strict direct heard via n-M or GATE or ECHO or RELAY *)
      */
      while (p<len) {
         /* more vias */
         ++p;
         while (p<len && b[p]!=',') ++p;
         if (b[p-1UL]=='*') return 0;
      }
   }
   /* is repeated */
   return 1;
} /* end Heard() */


static void IncHeard(pHEARD ph, char up, uint32_t livetime)
{
   uint32_t t;
   struct HEARD * anonym;
   if (livetime>0UL) {
      { /* with */
         struct HEARD * anonym = ph;
         t = systime/(livetime/48UL)+49UL;
         /*WrInt(t, 10); WrInt(cntt, 10); WrInt(ORD(up), 10); WrLn; */
         if (anonym->cntt+49UL<t) anonym->cntt = t-49UL;
         while (anonym->cntt<t) {
            ++anonym->cntt;
            anonym->cnt[anonym->cntt%49UL] = 0U;
         }
         t = anonym->cntt%49UL;
         if (up && anonym->cnt[t]<65535U) ++anonym->cnt[t];
      }
   }
/*FOR t:=0 TO HIGH(ph^.cnt) DO WrInt(ORD(ph^.cnt[t]), 2) END; WrLn; */
} /* end IncHeard() */


static void Wx(float * data, char * typ0, char b[],
                uint32_t b_len, uint32_t p)
{
   int32_t t;
   char sig;
   if (((b[p]!='/' && b[p]!='@') && b[p]!='!') && b[p]!='=') return;
   if (b[p]=='/' || b[p]=='@') p += 7UL;
   ++p;
   if ((uint8_t)b[p]>='0' && (uint8_t)b[p]<='9') p += 30UL;
   else if ((uint8_t)b[p]>='/') p += 17UL;
   if (b[p]!='t') return;
   t = 0L;
   sig = 0;
   if (b[p+1UL]=='-') sig = 1;
   else if ((uint8_t)b[p+1UL]>='0' && (uint8_t)b[p+1UL]<='9') {
      t = (int32_t)(((uint32_t)(uint8_t)b[p+1UL]-48UL)*100UL);
   }
   else return;
   if ((uint8_t)b[p+2UL]>='0' && (uint8_t)b[p+2UL]<='9') {
      t += (int32_t)(((uint32_t)(uint8_t)b[p+2UL]-48UL)*10UL);
   }
   else return;
   if ((uint8_t)b[p+3UL]>='0' && (uint8_t)b[p+3UL]<='9') {
      t += (int32_t)((uint32_t)(uint8_t)b[p+3UL]-48UL);
   }
   else return;
   if (sig) t = -t;
   *typ0 = 'C';
   *data = X2C_DIVR((float)(t-32L),1.8f);
} /* end Wx() */


static void DirectPos(const char b[], uint32_t b_len,
                struct aprspos_POSITION * pos, char * sym,
                char * symt, char * typ0, float * data)
{
   uint32_t m;
   uint32_t p;
   struct aprspos_POSITION posn;
   char symt1;
   char sym1;
   char postyp;
   MONCALL dc;
   FRAMEBUF com;
   /* OE0AAA>ABCDEF-2,....:...*/
   /*        ^mice         ^payload*/
   p = 0UL;
   while (p<b_len-1 && b[p]!='>') ++p;
   m = p+1UL;
   while (p<b_len-1 && b[p]!=':') ++p;
   ++p;
   if (p<b_len-1 && b[p]!=';') {
      /* objects say wrong pos */
      vspeed = X2C_max_longcard;
      aprspos_GetPos(&posn, &vspeed, &vcourse, &valt, &sym1, &symt1, b,
                b_len, m, p, com, 512ul, &postyp);
      dc[0U] = b[m];
      dc[1U] = b[m+1UL];
      dc[2U] = b[m+2UL];
      dc[3U] = b[m+3UL];
      dc[4U] = b[m+4UL];
      dc[5U] = b[m+5UL];
      dc[6U] = 0;
      aprspos_GetSym(dc, 10ul, &sym1, &symt1);
      *typ0 = 0;
      if (aprspos_posvalid(posn)) {
         *pos = posn;
         *sym = sym1;
         *symt = symt1;
         if (*sym=='_') Wx(data, typ0, b, b_len, p);
         else if (vspeed<X2C_max_longcard) {
            *typ0 = 'S';
            *data = (float)vspeed*1.852f;
         }
      }
   }
} /* end DirectPos() */


static uint32_t MHcount(pHEARD ph, uint32_t maxtime)
{
   uint32_t i0;
   uint32_t ci;
   /* clean count array */
   struct HEARD * anonym;
   IncHeard(ph, 0, maxtime);
   { /* with */
      struct HEARD * anonym = ph;
      ci = 0UL;
      for (i0 = 0UL; i0<=48UL; i0++) {
         ci += (uint32_t)anonym->cnt[i0];
      } /* end for */
   }
   return ci;
} /* end MHcount() */


static void getval(const char s[], uint32_t s_len, uint32_t * i0,
                int32_t * v)
{
   char m;
   ++*i0;
   *v = 0L;
   m = s[*i0]=='-';
   if (m) ++*i0;
   while ((*i0<s_len-1 && (uint8_t)s[*i0]>='0') && (uint8_t)s[*i0]<='9') {
      *v =  *v*10L+(int32_t)((uint32_t)(uint8_t)s[*i0]-48UL);
      ++*i0;
   }
   while (*i0<s_len-1 && (uint8_t)s[*i0]>' ') ++*i0;
   while (*i0<s_len-1 && s[*i0]==' ') ++*i0;
   if (m) *v = -*v;
} /* end getval() */


static void AddHeard(pHEARD * table, uint32_t maxtime, const MONCALL from,
                uint32_t fromport, const char buf[],
                uint32_t buf_len, char * ungat,
                char setungat)
{
   uint32_t j;
   uint32_t i0;
   int32_t res0;
   pHEARD po;
   pHEARD ph;
   struct HEARD * anonym;
   struct HEARD * anonym0;
   po = 0;
   ph = *table;
   if (mhperport) {
      while (ph && (ph->fromrx!=fromport || !aprsstr_StrCmp(ph->call, 10ul,
                from, 10ul))) {
         po = ph;
         ph = ph->next;
      }
   }
   else {
      while (ph && !aprsstr_StrCmp(ph->call, 10ul, from, 10ul)) {
         po = ph;
         ph = ph->next;
      }
   }
   if (ph) {
      /* entry found */
      if (po) po->next = ph->next;
   }
   else {
      /* new entry */
      po = 0;
      ph = *table;
      if (ph) {
         i0 = 0UL;
         while (ph->next) {
            ++i0;
            po = ph;
            ph = ph->next;
         }
         if (i0>=500UL || ph->time0+maxtime<systime) {
            /* old or table full */
            if (po) po->next = ph->next;
         }
         else ph = 0;
      }
      if (ph==0) {
         osic_alloc((char * *) &ph, sizeof(struct HEARD));
         if (ph==0 && po) {
            ph = po->next;
            po->next = 0;
         }
      }
      if (ph) {
         { /* with */
            struct HEARD * anonym = ph;
            memcpy(anonym->call,from,10u);
            anonym->cntt = 0UL;
            anonym->position.long0 = 0.0f;
            anonym->position.lat = 0.0f;
            anonym->sym = 0;
            anonym->symt = 0;
            anonym->datatyp = 0;
            anonym->ungate = 0;
            anonym->fromrx = 0UL;
            anonym->head[0U] = 0;
            anonym->data = 0.0f;
         }
      }
   }
   if (ph) {
      if (ph!=*table) {
         ph->next = *table;
         *table = ph;
      }
      IncHeard(ph, !setungat, maxtime);
      { /* with */
         struct HEARD * anonym0 = ph;
         anonym0->time0 = systime;
         if (setungat) {
            anonym0->ungate = *ungat;
            if (!*ungat && MHcount(ph, maxtime)==0UL) {
               ph->time0 = 0UL; /* delete 0 count entry */
            }
         }
         else {
            *ungat = ph->ungate;
            DirectPos(buf, buf_len, &anonym0->position, &anonym0->sym,
                &anonym0->symt, &anonym0->datatyp, &anonym0->data);
            i0 = 0UL;
            while ((i0<buf_len-1 && buf[i0]!='>') && buf[i0]) ++i0;
            ++i0;
            j = 0UL;
            while ((((j<40UL && i0<buf_len-1) && buf[i0]!=':') && buf[i0])
                && !(buf[i0]==',' && buf[i0+1UL]=='q')) {
               anonym0->head[j] = buf[i0];
               ++j;
               ++i0;
            }
            anonym0->head[j] = 0;
            anonym0->fromrx = fromport;
            anonym0->txd = 0U;
            anonym0->level = 0;
            anonym0->quali = 0U;
            if (udp2[0U] && udp2[1U]) {
               i0 = 2UL;
               while (i0<99UL && udp2[i0]) {
                  switch ((unsigned)udp2[i0]) {
                  case 'T':
                     getval(udp2, 100ul, &i0, &res0);
                     anonym0->txd = (uint16_t)res0;
                     break;
                  case 'V':
                     getval(udp2, 100ul, &i0, &res0);
                     anonym0->level = (signed char)res0;
                     break;
                  case 'Q':
                     getval(udp2, 100ul, &i0, &res0);
                     anonym0->quali = (uint8_t)res0;
                     break;
                  default:;
                     getval(udp2, 100ul, &i0, &res0);
                     break;
                  } /* end switch */
               }
            }
         }
      }
   }
   else *ungat = 0;
} /* end AddHeard() */


static void delmsg(pMESSAGE md)
{
   pMESSAGE mp;
   if (md==messages) messages = md->next;
   else {
      mp = messages;
      for (;;) {
         if (mp==0) return;
         if (mp->next==md) {
            mp->next = md->next;
            break;
         }
         mp = mp->next;
      }
   }
   if (verb) {
      osi_WrStrLn("---delete:", 11ul);
      Showmsg(md);
   }
   osic_free((char * *) &md, sizeof(struct MESSAGE));
} /* end delmsg() */


static uint32_t exp0(uint32_t n)
{
   if (n>0UL) --n;
   if (n>9UL) n = 9UL;
   return (uint32_t)(1UL<<n);
} /* end exp() */


static void RfTimer(void)
{
   pMESSAGE mp;
   pMESSAGE mo;
   char try0;
   uint32_t ht;
   uint32_t oldest;
   uint32_t totx;
   uint32_t maxi;
   struct MESSAGE * anonym;
   if (lastrfsent+rfquiet<=systime) {
      maxi = 0UL;
      for (;;) {
         oldest = X2C_max_longcard;
         mp = 0;
         mo = messages;
         while (mo) {
            if (mo->chkt!=systime && mo->txtime<oldest) {
               mp = mo;
               oldest = mp->txtime;
            }
            mo = mo->next;
         }
         if (mp==0) break;
         { /* with */
            struct MESSAGE * anonym = mp;
            anonym->chkt = systime;
            try0 = anonym->gentime+msgsendtime>systime && anonym->txtime+rfquiet*exp0(anonym->retryc)
                <systime;
            totx = 0UL;
            if (try0) {
               if (anonym->acked) {
                  try0 = (anonym->acksrc && anonym->retryc==0UL)
                && FindHeard(hearddir, anonym->from, &totx)>0UL;
               }
               else {
                  ht = FindHeard(hearddir, anonym->to, &totx);
                  try0 = 0;
                  if (ht>0UL) {
                     if (anonym->ack[0U]==0) {
                        try0 = anonym->retryc<1UL; /* msg with no ack */
                     }
                     else if (anonym->retryc<2UL) try0 = ht<3600UL;
                     else if (anonym->retryc<3UL) try0 = ht<1800UL;
                     else if (anonym->retryc<4UL) try0 = ht<900UL;
                     else try0 = ht<60UL;
                     if (try0) {
                        mo = messages;
                        for (;;) {
                           if (mo==mp) break;
                           if ((aprsstr_StrCmp(mo->to, 10ul, anonym->to,
                10ul) && mo->gentime+msgsendtime>systime)
                && (mo->ack[0U] && !mo->acked || mo->ack[0U]
                ==0 && mo->retryc<1UL)) {
                              try0 = 0;
                              break;
                           }
                           mo = mo->next;
                        }
                     }
                  }
               }
            }
            if (try0) {
               SendMsg(mp, totx);
               lastrfsent = systime;
               /*
                         mo:=messages;
                         WHILE mo<>NIL DO
                           IF StrCmp(mo^.to,
                to) & NOT acked THEN mo^.chkt:=systime END;
                (* 1 msg to 1 call *)
                           mo:=mo^.next;
                         END;
               */
               ++maxi;
            }
            /*
                    IF (ack[0]=0C) & (retryc>=UNACKRET)
                OR acked & (msg2rf+msg2net+ack2rf+ack2net=0) 
                    THEN ht:=purgeunack ELSE ht:=purgemsg END;
                (* fast if unack & retried out or acked & never sent *)
            */
            if (anonym->ack[0U]==0) {
               if (anonym->retryc>=1UL) ht = purgeunacksent;
               else ht = purgeunack;
            }
            else if (anonym->acked) ht = purgeacked;
            else ht = purgemsg;
            if (anonym->gentime+ht<systime) {
               delmsg(mp); /* purge message */
            }
         }
         if (maxi>=maxatonce) break;
      }
   }
} /* end RfTimer() */


static void SendNet(void)
{
   pMESSAGE mp;
   while (netmhout!=netmhin) {
      mp = messages;
      while (mp) {
         if (mp->retryc==0UL) {
            if (mp->acked) {
               if (mp->acksrc==udpgate4_DIR && aprsstr_StrCmp(mp->from, 10ul,
                 netmh[netmhout], 10ul)) {
                  SendMsg(mp, 0UL); /* send only 1 a time */
                  return;
               }
            }
            else if ((mp->src==udpgate4_DIR || aprsstr_StrCmp(servercall,
                10ul, mp->from, 10ul)) && aprsstr_StrCmp(mp->to, 10ul,
                netmh[netmhout], 10ul)) {
               SendMsg(mp, 0UL);
               if (mp->ack[0U]) {
                  /* no more to this user if unack */
                  netmhout = (netmhout+1UL)%100UL;
               }
               return;
            }
         }
         /* send only 1 a time */
         mp = mp->next;
      }
      netmhout = (netmhout+1UL)%100UL;
   }
} /* end SendNet() */


static void Stoack(pMESSAGE mp, char rej, uint8_t path)
{
   struct MESSAGE * anonym;
   { /* with */
      struct MESSAGE * anonym = mp;
      if (!anonym->acked) {
         /* ack first time */
         anonym->acked = 1;
         anonym->reject = rej;
         anonym->acksrc = path;
         anonym->retryc = 0UL;
         anonym->txtime = 0UL;
      }
      else if (path>anonym->acksrc) anonym->acksrc = path;
   }
} /* end Stoack() */


static void Ackback(pMESSAGE tomp)
/* mark all older messages before an now acked */
{
   pMESSAGE mp;
   struct MESSAGE * anonym;
   mp = messages;
   while (mp!=tomp) {
      { /* with */
         struct MESSAGE * anonym = mp;
         if ((aprsstr_StrCmp(tomp->from, 10ul, anonym->from,
                10ul) && aprsstr_StrCmp(tomp->to, 10ul, anonym->to,
                10ul)) && anonym->gentime+dupetime<systime) {
            if (!anonym->acked) {
               anonym->acked = 1;
               anonym->acksrc = udpgate4_OBSOLET;
            }
            anonym->ackackt = 1;
         }
         mp = anonym->next;
      }
   }
} /* end Ackback() */


static char cmpmsg(MSGTEXT a, MSGTEXT b)
/* compare visable characters */
{
   uint32_t j;
   uint32_t i0;
   MSGTEXT tmp;
   MSGTEXT tmp0;
   a = (char *)memcpy(tmp,a,68u);
   b = (char *)memcpy(tmp0,b,68u);
   i0 = 0UL;
   j = 0UL;
   while (i0<=67UL && a[i0]) {
      if ((uint8_t)a[i0]>' ') {
         a[j] = a[i0];
         ++j;
      }
      ++i0;
   }
   if (j<=67UL) a[j] = 0;
   i0 = 0UL;
   j = 0UL;
   while (i0<=67UL && b[i0]) {
      if ((uint8_t)b[i0]>' ') {
         b[j] = b[i0];
         ++j;
      }
      ++i0;
   }
   if (j<=67UL) b[j] = 0;
   return aprsstr_StrCmp(a, 68ul, b, 68ul);
} /* end cmpmsg() */

#define udpgate4_REPACK "}"


static void Stomsg(MONCALL fromcall, MONCALL tocall, MSGTEXT msg,
                ACKTEXT ackt, char isak, char rej,
                char query, uint8_t path)
{
   pMESSAGE mi;
   pMESSAGE mp;
   uint32_t cnt;
   REPLYACK rack;
   struct MESSAGE * anonym;
   struct MESSAGE * anonym0;
   MONCALL tmp;
   MONCALL tmp0;
   MSGTEXT tmp1;
   ACKTEXT tmp2;
   fromcall = (char *)memcpy(tmp,fromcall,10u);
   tocall = (char *)memcpy(tmp0,tocall,10u);
   msg = (char *)memcpy(tmp1,msg,68u);
   ackt = (char *)memcpy(tmp2,ackt,5u);
   if (verb && isak) osi_WrStrLn("---isak", 8ul);
   if (aprsstr_StrCmp(fromcall, 10ul, tocall, 10ul)) return;
   /* msg to him self ... */
   msg[67U] = 0;
   rack[0U] = 0;
   if (aprsstr_Length(ackt, 5ul)>=3UL && ackt[2U]=='}') {
      /* replyack xx}xx */
      rack[0U] = ackt[3U]; /* split ack text xx}yy to xx} yy */
      rack[1U] = ackt[4U];
      ackt[3U] = 0;
   }
   mp = 0;
   cnt = 0UL;
   mi = messages;
   while (mi) {
      { /* with */
         struct MESSAGE * anonym = mi;
         if (((aprsstr_StrCmp(ackt, 5ul, anonym->ack,
                5ul) && aprsstr_StrCmp(fromcall, 10ul, anonym->from,
                10ul)) && aprsstr_StrCmp(tocall, 10ul, anonym->to,
                10ul)) && ((isak || anonym->text[0U]==0) || cmpmsg(msg,
                anonym->text))) {
            mp = mi;
            memcpy(anonym->replyack,rack,2u);
                /* may change in retransmissions */
            if (isak) {
               Stoack(mi, rej, path);
               Ackback(mi);
            }
            else {
               if (anonym->acked && path==udpgate4_DIR) {
                  /* is acked resend ack */
                  if (anonym->retryc>0UL) --anonym->retryc;
                  anonym->txtime = 0UL;
               }
               if (anonym->text[0U]==0) {
                  /* ack came before message */
                  memcpy(anonym->text,msg,68u);
                  if (anonym->acked) Ackback(mi);
               }
               if (path>anonym->src) anonym->src = path;
            }
         }
         if ((((rack[0U] && rack[0U]==anonym->ack[0U])
                && (rack[1U]==anonym->ack[1U] || rack[1U]
                ==0 && anonym->ack[1U]=='}')) && aprsstr_StrCmp(fromcall,
                10ul, anonym->to, 10ul)) && aprsstr_StrCmp(tocall, 10ul,
                anonym->from, 10ul)) Stoack(mi, rej, path);
         mi = anonym->next;
      }
      ++cnt;
   }
   if (verb && mp) {
      osi_WrStr("msg found ", 11ul);
      Showmsg(mp);
   }
   if (mp==0) {
      /* new msg */
      if (cnt<maxmsg) {
         osic_alloc((char * *) &mp, sizeof(struct MESSAGE));
      }
      if (mp==0 && messages) {
         mp = messages; /* recycle oldest */
         messages = messages->next;
      }
      if (mp) {
         /* append to end of list */
         memset((char *)mp,(char)0,sizeof(struct MESSAGE));
         mp->next = 0;
         if (messages==0) messages = mp;
         else {
            mi = messages;
            while (mi->next) mi = mi->next;
            mi->next = mp;
         }
         { /* with */
            struct MESSAGE * anonym0 = mp;
            memcpy(anonym0->from,fromcall,10u);
            memcpy(anonym0->to,tocall,10u);
            memcpy(anonym0->text,msg,68u);
            memcpy(anonym0->ack,ackt,5u);
            memcpy(anonym0->replyack,rack,2u);
            anonym0->gentime = systime;
            if (path==udpgate4_DIR) anonym0->txtime = systime;
            else anonym0->txtime = 0UL;
            anonym0->acked = isak;
            anonym0->reject = rej;
            anonym0->src = path;
            anonym0->acksrc = path;
            anonym0->queryrep = query;
         }
      }
      if (verb) {
         if (path==udpgate4_DIR) osi_WrStr("new direct", 11ul);
         else if (path==udpgate4_INDIR) osi_WrStr("new rf", 7ul);
         else osi_WrStr("new net", 8ul);
         osi_WrStr(" msg#", 6ul);
         osic_WrINT32(cnt, 1UL);
         osi_WrStr(" ", 2ul);
         Showmsg(mp);
      }
   }
/*
  IF mp<>NIL THEN Ackback(mp) END;
                (* all before is thru and ack is at home so mark as done *)
*/
} /* end Stomsg() */


static void postoloc(char loc[], uint32_t loc_len,
                struct aprspos_POSITION pos)
{
   uint32_t bc;
   uint32_t lc;
   float br;
   float lr;
   lr = (pos.long0*5.7295779513082E+1f+180.0f)*2880.0f;
   if (lr<=0.0f) lr = 0.0f;
   lc = (uint32_t)X2C_TRUNCC(lr,0UL,X2C_max_longcard);
   br = (pos.lat*5.7295779513082E+1f+90.0f)*5760.0f;
   if (br<=0.0f) br = 0.0f;
   bc = (uint32_t)X2C_TRUNCC(br,0UL,X2C_max_longcard);
   loc[0UL] = (char)(65UL+lc/57600UL);
   loc[1UL] = (char)(65UL+bc/57600UL);
   loc[2UL] = (char)(48UL+(lc/5760UL)%10UL);
   loc[3UL] = (char)(48UL+(bc/5760UL)%10UL);
   loc[4UL] = (char)(65UL+(lc/240UL)%24UL);
   loc[5UL] = (char)(65UL+(bc/240UL)%24UL);
   loc[6UL] = (char)(48UL+(lc/24UL)%10UL);
   loc[7UL] = (char)(48UL+(bc/24UL)%10UL);
   loc[8UL] = (char)(65UL+lc%24UL);
   loc[9UL] = (char)(65UL+bc%24UL);
   loc[10UL] = 0;
} /* end postoloc() */


static void degtostr(char s[], uint32_t s_len, float d,
                char posc, char negc)
{
   uint32_t n;
   char c;
   char tmp;
   d = X2C_DIVR(d,1.7453292519444E-2f);
   if (d<0.0f) {
      d = -d;
      c = negc;
   }
   else c = posc;
   n = (uint32_t)X2C_TRUNCC(d,0UL,X2C_max_longcard);
   aprsstr_IntToStr((int32_t)n, 1UL, s, s_len);
   aprsstr_Append(s, s_len, ".", 2ul);
   n = (uint32_t)X2C_TRUNCC((d-(float)n)*3600.0f,0UL,X2C_max_longcard);
   aprsstr_Append(s, s_len, (char *)(tmp = (char)(48UL+n/600UL),
                &tmp), 1u/1u);
   aprsstr_Append(s, s_len, (char *)(tmp = (char)(48UL+(n%600UL)/60UL),
                &tmp), 1u/1u);
   aprsstr_Append(s, s_len, ".", 2ul);
   aprsstr_Append(s, s_len, (char *)(tmp = (char)(48UL+(n%60UL)/10UL),
                &tmp), 1u/1u);
   aprsstr_Append(s, s_len, (char *)(tmp = (char)(48UL+n%10UL),&tmp),
                 1u/1u);
   aprsstr_Append(s, s_len, (char *) &c, 1u/1u);
} /* end degtostr() */

#define udpgate4_PARPOS 6


static void Ungate(const MONCALL mycall, const char msg[],
                uint32_t msg_len, uint8_t path)
{
   uint32_t p;
   uint32_t n;
   uint32_t i0;
   MONCALL hn;
   MONCALL h;
   MSGTEXT answ;
   char un;
   pHEARD ph;
   char tmp;
   ACKTEXT tmp0;
   /* ?APRS#, ?APRS# -9, ?APRS# 9, ?APRS# CALL*/
   memcpy(h,mycall,10u);
   i0 = 0UL;
   for (;;) {
      if (i0>9UL) break;
      if (h[i0]=='-') {
         h[i0] = 0;
         break;
      }
      ++i0;
   }
   memcpy(hn,h,10u);
   i0 = 6UL;
   skipblank(msg, msg_len, &i0);
   p = i0;
   un = msg[i0]=='-';
   if (un) ++i0;
   if (GetSec(msg, msg_len, &i0, &n)==0L && n<=15UL) {
      if (n>0UL) {
         aprsstr_Append(h, 10ul, "-", 2ul);
         if (n>=10UL) aprsstr_Append(h, 10ul, "1", 2ul);
         aprsstr_Append(h, 10ul, (char *)(tmp = (char)(n%10UL+48UL),
                &tmp), 1u/1u);
      }
      AddHeard(&hearddir, Max(heardtime, heardtimew), h, 0UL, "", 1ul, &un,
                1);
   }
   else {
      n = aprsstr_Length(msg, msg_len);
      i0 = 0UL;
      while ((i0<=9UL && p<n) && (uint8_t)msg[p]>' ') {
         hn[i0] = msg[p];
         ++i0;
         ++p;
      }
      if (i0>0UL && i0<=9UL) hn[i0] = 0;
   }
   strncpy(answ,"Not Gated:",68u);
   un = 1;
   ph = hearddir;
   for (;;) {
      if (ph==0) break;
      if (ph->ungate && aprsstr_InStr(ph->call, 10ul, hn, 10ul)==0L) {
         if (aprsstr_Length(answ, 68ul)+aprsstr_Length(ph->call,
                10ul)>=64UL) {
            aprsstr_Append(answ, 68ul, "...", 4ul);
            break;
         }
         aprsstr_Append(answ, 68ul, " ", 2ul);
         aprsstr_Append(answ, 68ul, ph->call, 10ul);
         un = 0;
      }
      ph = ph->next;
   }
   if (un) {
      aprsstr_Append(answ, 68ul, " None", 6ul);
      if (n<=6UL) {
         aprsstr_Append(answ, 68ul, " (igate your ssid off/on: ?APRS# -5 / 5)\
", 41ul);
      }
   }
   Stomsg(servercall, mycall, answ, *(ACKTEXT *)memcpy(&tmp0,"",1u), 0, 0, 1,
                 path);
} /* end Ungate() */


static void Query(MONCALL fromcall, char msg[], uint32_t msg_len,
                const char buf[], uint32_t buf_len, uint8_t path)
{
   pMESSAGE mp;
   char no;
   MSGTEXT s;
   MSGTEXT mh;
   MONCALL callh;
   uint32_t len;
   uint32_t i0;
   pHEARD hn;
   pHEARD hp;
   char cmd;
   float maxdist;
   float dist;
   MONCALL tmp;
   ACKTEXT tmp0;
   MSGTEXT tmp1;
   fromcall = (char *)memcpy(tmp,fromcall,10u);
   X2C_PCOPY((void **)&msg,msg_len);
   cmd = 0;
   len = aprsstr_Length(msg, msg_len);
   i0 = 1UL;
   while (i0<len) {
      msg[i0] = X2C_CAP(msg[i0]);
      ++i0;
   }
   if ((((msg[1UL]=='A' && msg[2UL]=='P') && msg[3UL]=='R') && msg[4UL]=='S')
                 && (msg[6UL]==0 || msg[6UL]==' ')) cmd = msg[5UL];
   else if ((msg[1UL]=='M' && msg[2UL]=='H')
                && (msg[3UL]==0 || msg[3UL]=='?')) cmd = 'D';
   else if (((((msg[1UL]=='P' && msg[2UL]=='I') && msg[3UL]=='N')
                && msg[4UL]=='G') && msg[5UL]=='?') && msg[6UL]==0) {
      cmd = 'T';
   }
   else if ((((msg[1UL]=='H' && msg[2UL]=='E') && msg[3UL]=='L')
                && msg[4UL]=='P') && msg[5UL]==0) cmd = 'h';
   else if (((msg[1UL]=='V' && msg[2UL]=='E') && msg[3UL]=='R')
                && (msg[4UL]==0 || msg[4UL]=='?')) cmd = 'v';
   else if ((msg[1UL]=='D' && msg[2UL]=='X') && msg[3UL]==0) cmd = 'x';
   if (cmd=='M') {
      /* activate messages for user */
      no = verb;
      mp = messages;
      while (mp) {
         if (!mp->acked && aprsstr_StrCmp(fromcall, 10ul, mp->to, 10ul)) {
            mp->retryc = 0UL;
            if (verb) {
               no = 0;
               osi_WrStr("query msg activated:", 21ul);
               Showmsg(mp);
            }
         }
         mp = mp->next;
      }
      if (no) osi_WrStrLn("query msg not found", 20ul);
   }
   else if (cmd=='T') {
      /* return message path */
      i0 = 0UL;
      strncpy(mh,"Path - ",68u);
      while ((i0+7UL<67UL && buf[i0]!=':') && buf[i0]) {
         mh[i0+7UL] = buf[i0];
         ++i0;
      }
      mh[i0+7UL] = 0;
      Stomsg(servercall, fromcall, mh, *(ACKTEXT *)memcpy(&tmp0,"",1u), 0, 0,
                 1, path);
   }
   else if (cmd=='D') {
      /* return mh table */
      strncpy(mh,"Directs=",68u);
      hn = hearddir;
      while ((hn && hn->time0+heardtimew>systime) && aprsstr_Length(mh,
                68ul)+aprsstr_Length(hn->call, 10ul)+1UL<67UL) {
         if (!hn->ungate) {
            aprsstr_Append(mh, 68ul, " ", 2ul);
            aprsstr_Append(mh, 68ul, hn->call, 10ul);
         }
         hn = hn->next;
      }
      Stomsg(servercall, fromcall, mh, *(ACKTEXT *)memcpy(&tmp0,"",1u), 0, 0,
                 1, path);
   }
   else if (cmd=='x' || cmd=='H') {
      if (cmd=='H') {
         for (i0 = 0UL; i0<=9UL; i0++) {
            callh[i0] = X2C_CAP(msg[i0+7UL]);
         } /* end for */
      }
      maxdist = 0.0f;
      hn = 0;
      hp = hearddir;
      while (hp) {
         if (((hp->time0+heardtimew>systime && !hp->ungate)
                && aprspos_posvalid(hp->position)) && aprspos_posvalid(home))
                 {
            dist = aprspos_distance(home, hp->position);
            if (cmd=='H') {
               if (aprsstr_StrCmp(callh, 10ul, hp->call, 10ul)) {
                  hn = hp;
                  maxdist = dist;
               }
            }
            else if (dist>maxdist) {
               maxdist = dist;
               hn = hp;
            }
         }
         hp = hp->next;
      }
      if (hn) {
         strncpy(mh,"DX: ",68u);
         aprsstr_Append(mh, 68ul, hn->call, 10ul);
         aprsstr_Append(mh, 68ul, " ", 2ul);
         degtostr(s, 68ul, hn->position.lat, 'N', 'S');
         aprsstr_Append(mh, 68ul, s, 68ul);
         aprsstr_Append(mh, 68ul, " ", 2ul);
         degtostr(s, 68ul, hn->position.long0, 'E', 'W');
         aprsstr_Append(mh, 68ul, s, 68ul);
         aprsstr_Append(mh, 68ul, " ", 2ul);
         aprsstr_IntToStr((int32_t)(uint32_t)X2C_TRUNCC(maxdist+0.5f,0UL,
                X2C_max_longcard), 1UL, s, 68ul);
         aprsstr_Append(mh, 68ul, s, 68ul);
         aprsstr_Append(mh, 68ul, "km ", 4ul);
         aprsstr_TimeToStr(hn->time0%86400UL, s, 68ul);
         aprsstr_Append(mh, 68ul, s, 68ul);
      }
      else strncpy(mh,"No position available",68u);
      Stomsg(servercall, fromcall, mh, *(ACKTEXT *)memcpy(&tmp0,"",1u), 0, 0,
                 1, path);
   }
   else if (cmd=='S') {
      Stomsg(servercall, fromcall, *(MSGTEXT *)memcpy(&tmp1,"udpgate(c) 0.63 \
Msg S&F Relay",30u), *(ACKTEXT *)memcpy(&tmp0,"",1u), 0, 0, 1, path);
   }
   else if (cmd=='v') {
      Stomsg(servercall, fromcall, *(MSGTEXT *)memcpy(&tmp1,
                "udpgate(c) 0.63",16u), *(ACKTEXT *)memcpy(&tmp0,"",1u), 0,
                0, 1, path);
   }
   else if (cmd=='h') {
      Stomsg(servercall, fromcall, *(MSGTEXT *)memcpy(&tmp1,"Cmd: ?APRS[D/H/M\
/P/S/T/#] ?DX ?MH ?VER? ?PING?",47u), *(ACKTEXT *)memcpy(&tmp0,"",1u), 0, 0,
                1, path);
   }
   else if (cmd=='P') {
      Netbeacon(mh, 68ul, 0, 0);
      Stomsg(servercall, fromcall, mh, *(ACKTEXT *)memcpy(&tmp0,"",1u), 0, 0,
                 1, path);
   }
   else if (cmd=='#') Ungate(fromcall, msg, msg_len, path);
   X2C_PFREE(msg);
} /* end Query() */


static void gatetoviaheard(const char b[], uint32_t b_len,
                const MONCALL fromcall, const MONCALL tocall)
{
   pUDPSOCK u;
   uint32_t void0;
   uint32_t po;
   if (!aprsstr_StrCmp(fromcall, 10ul, tocall, 10ul) && FindHeard(heardvia,
                tocall, &po)>0UL) {
      u = porttosock(po);
      if ((u && u->torfradius>0.0f) && FindHeard(hearddir, tocall,
                &void0)==0UL) NetToRf(b, b_len, po);
   }
} /* end gatetoviaheard() */


static char iscall(const char b[], uint32_t b_len,
                uint32_t p)
{
   uint32_t i0;
   uint32_t nu;
   uint32_t li;
   char c;
   li = 0UL;
   nu = 0UL;
   for (i0 = 0UL; i0<=2UL; i0++) {
      c = b[p+i0];
      if ((uint8_t)c>='A' && (uint8_t)c<='Z') ++li;
      else if ((uint8_t)c>='0' && (uint8_t)c<='9') {
         if (i0==0UL) ++li;
         else ++nu;
      }
   } /* end for */
   /*
   IO.WrCard(li,9);IO.WrCard(nu,9); IO.WrStrLn("<3>");
   */
   return (nu+li==3UL && nu>=1UL) && nu<=2UL;
} /* end iscall() */


static void getack(const char s[], uint32_t s_len, uint32_t p,
                ACKTEXT a)
{
   uint32_t i0;
   i0 = 0UL;
   while ((p<=s_len-1 && (uint8_t)s[p]>' ') && (uint8_t)s[p]<(uint8_t)
                '\200') {
      a[i0] = s[p];
      ++p;
      ++i0;
      if (i0>4UL) return;
   }
   a[i0] = 0;
} /* end getack() */

#define udpgate4_HEADEREND ":"

#define udpgate4_FROMEND ">"

#define udpgate4_USERACK "{"

#define udpgate4_NOARCHIVE "!x!"


static void Getmsg(const char b[], uint32_t b_len, uint32_t rxport,
                char goodpath, char * ungate)
/* get user msg and ack out of aprsis stream */
{
   uint32_t po;
   uint32_t void0;
   uint32_t p;
   uint32_t len;
   uint32_t pf;
   uint32_t pm;
   MSGTEXT msg;
   ACKTEXT ack;
   MONCALL hfrom;
   MONCALL tocall;
   MONCALL fromcall;
   char c;
   char tomh;
   char dir;
   char trust;
   char isrej;
   char isack;
   uint8_t path;
   *ungate = 0;
   if ((b[0UL]==0 || rxport && !iscall(b, b_len, 0UL)) || aprsstr_InStr(b,
                b_len, "!x!", 4ul)>=0L) return;
   dir = Heard(b, b_len, hfrom, &trust);
   if (hfrom[0U]) {
      if (rxport) {
         if ((heardtime>0UL && dir) && trust) {
            AddHeard(&hearddir, Max(heardtime, heardtimew), hfrom, rxport, b,
                 b_len, ungate, 0);
         }
         else if (heardtimevia>0UL) {
            AddHeard(&heardvia, heardtimevia, hfrom, rxport, b, b_len,
                ungate, 0);
         }
      }
   }
   if (maxmsg==0UL && viacall[0U]==0) return;
   /* mh server only */
   len = aprsstr_Length(b, b_len);
   pm = 0UL;
   do {
      pf = pm;
      for (;;) {
         if (pm>=len) return;
         if (b[pm]==':') break;
         ++pm;
      }
      pm += 2UL;
   } while (!(pm>len || b[pm-1UL]!='}'));
   /* skip thirdparty headers */
   --pm;
   tomh = sendnetmsg;
   if (((pm+10UL<len && b[pm]==':') && b[pm+10UL]==':') && iscall(b, b_len,
                pm+1UL)) {
      /* user message */
      /* to */
      cpcall(tocall, 10ul, b, b_len, pm+1UL);
      if (iscall(b, b_len, pf) && maxmsg) {
         /* mail storage on & not from a special (no)call */
         /* from */
         cpcall(fromcall, 10ul, b, b_len, pf);
         /* text */
         p = 0UL;
         pm += 11UL;
         for (;;) {
            if (pm>=len) break;
            c = b[pm];
            if ((p>=67UL || (uint8_t)c<' ') || c=='{') break;
            msg[p] = c;
            ++p;
            ++pm;
         }
         msg[p] = 0;
         /* ack */
         isack = 0;
         ack[0U] = 0;
         isrej = (msg[0U]=='r' && msg[1U]=='e') && msg[2U]=='j';
         if ((msg[0U]=='a' && msg[1U]=='c') && msg[2U]=='k' || isrej) {
            /* is ack or rej */
            getack(msg, 68ul, 3UL, ack);
            msg[0U] = 0;
            isack = 1;
         }
         else if (pm<len && b[pm]=='{') getack(b, b_len, pm+1UL, ack);
         path = udpgate4_NET;
         if (rxport) {
            path = udpgate4_INDIR;
            if ((dir && goodpath) && FindHeard(hearddir, fromcall,
                &void0)>0UL) path = udpgate4_DIR;
         }
         if (msg[0U]=='?') {
            if (aprsstr_StrCmp(tocall, 10ul, viacall,
                10ul) || aprsstr_StrCmp(tocall, 10ul, servercall, 10ul)) {
               Query(fromcall, msg, 68ul, b, b_len, path);
               tomh = 1;
            }
         }
         else if (isack) {
            Stomsg(tocall, fromcall, msg, ack, 1, isrej, 0, path);
         }
         else if (rxport || goodpath) {
            Stomsg(fromcall, tocall, msg, ack, 0, isrej, 0, path);
         }
         gatetoviaheard(b, b_len, fromcall, tocall);
      }
      else if (goodpath && FindHeard(hearddir, tocall, &po)>0UL) {
         NetToRf(b, b_len, po); /* not call from to local user */
      }
      if (verb) {
         osi_WrStr(" <", 3ul);
         p = 0UL;
         while (p<b_len-1 && b[p]) {
            if ((uint8_t)b[p]>=' ') {
               osi_WrStr((char *) &b[p], 1u/1u);
            }
            ++p;
         }
         osi_WrStrLn(">", 2ul);
      }
   }
   if (((tomh && hfrom[0U]) && rxport==0UL) && (netmhin+1UL)%100UL!=netmhout)
                 {
      memcpy(netmh[netmhin],hfrom,10u);
      netmhin = (netmhin+1UL)%100UL;
   }
} /* end Getmsg() */


static void getportname(uint32_t n, char s[], uint32_t s_len)
/* if not found return port number */
{
   pUDPSOCK u;
   uint32_t i0;
   if (n==0UL) aprsstr_Assign(s, s_len, "TCPIP", 6ul);
   else {
      u = udpsocks;
      i0 = 1UL;
      while (u && i0<n) {
         ++i0;
         u = u->next;
      }
      if (u==0 || u->portname[0U]==0) {
         aprsstr_IntToStr((int32_t)n, 1UL, s, s_len);
      }
      else aprsstr_Assign(s, s_len, u->portname, 10ul);
   }
} /* end getportname() */

#define udpgate4_SEP "|"
/* field separator */

#define udpgate4_SEPESC " "
/* set instead of SEP */

#define udpgate4_TEMPFN "~"
/* append to filename for temp file */

#define udpgate4_LINELEN 99
/* fill up lines to equal size */


static void App(uint32_t * p, char h[256], char s[],
                uint32_t s_len)
{
   uint32_t i0;
   X2C_PCOPY((void **)&s,s_len);
   i0 = 0UL;
   while (i0<=s_len-1 && s[i0]) {
      if (s[i0]!='|') h[*p] = s[i0];
      else h[*p] = ' ';
      ++*p;
      ++i0;
   }
   X2C_PFREE(s);
} /* end App() */


static void MHtoFile(void)
{
   char h[256];
   char h1[16];
   FILENAME fn;
   pHEARD hn;
   uint32_t t;
   int32_t fd;
   uint32_t cnt;
   uint32_t p;
   aprsstr_Assign(fn, 1024ul, mhfilename, 1024ul);
   aprsstr_Append(fn, 1024ul, "~", 2ul);
   fd = osi_OpenWrite(fn, 1024ul);
   if (fd<0L) return;
   cnt = mhfilelines;
   hn = hearddir;
   while (cnt>0UL && hn) {
      p = 0UL;
      if (hn->time0+heardtimew>systime && !hn->ungate) {
         App(&p, h, hn->call, 10ul);
         h[p] = '|';
         ++p;
         App(&p, h, (char *) &hn->symt, 1u/1u);
         App(&p, h, (char *) &hn->sym, 1u/1u);
         h[p] = '|';
         ++p;
         /*      IntToStr(hn^.fromrx, 1, h1); App(h1); */
         getportname(hn->fromrx, h1, 16ul);
         App(&p, h, h1, 16ul);
         h[p] = '|';
         ++p;
         t = hn->time0;
         if (systime<t || t==0UL) t = 0UL;
         else t = systime-t;
         aprsstr_IntToStr((int32_t)t, 1UL, h1, 16ul);
         App(&p, h, h1, 16ul);
         h[p] = '|';
         ++p;
         aprsstr_IntToStr((int32_t)MHcount(hn, heardtimew), 1UL, h1, 16ul);
         App(&p, h, h1, 16ul);
         h[p] = '|';
         ++p;
         if (aprspos_posvalid(hn->position) && aprspos_posvalid(home)) {
            aprsstr_FixToStr(aprspos_distance(home, hn->position)+0.05f, 2UL,
                 h1, 16ul);
            App(&p, h, h1, 16ul);
         }
         h[p] = '|';
         ++p;
         if (hn->datatyp=='C') {
            aprsstr_FixToStr(hn->data, 2UL, h1, 16ul);
            aprsstr_Append(h1, 16ul, "\260C", 3ul);
         }
         else if (hn->datatyp=='S') {
            aprsstr_FixToStr(hn->data, 0UL, h1, 16ul);
            aprsstr_Append(h1, 16ul, "kmh", 4ul);
         }
         else h1[0] = 0;
         App(&p, h, h1, 16ul);
         h[p] = '|';
         ++p;
         App(&p, h, hn->head, 41ul);
         h[p] = '\012';
         ++p;
         osi_WrBin(fd, (char *)h, 256u/1u, p);
      }
      hn = hn->next;
      --cnt;
   }
   osic_Close(fd);
   osi_Rename(fn, 1024ul, mhfilename, 1024ul);
} /* end MHtoFile() */

/*
PROCEDURE finddupe(s-:DUPESTRING):BOOLEAN;
VAR i, sum:CARDINAL;
    t:TIME;  
BEGIN
  i:=0;
  sum:=0;  
  WHILE (i<=HIGH(s)) & (s[i]<>0C) DO INC(sum, ORD(s[i]); INC(i) END;
                (* make simple hash *)

  sum:=sum MOD (HIGH(dupehashs)+1);
  t:=dupehashs[sum].time;
  IF systime>=t+dupetime THEN
    WITH dupestrings[dupewrite] DO
      str:=s;
      time:=systime;
      next:=dupewrite;
    END;
    dupehashs[sum].hash:=dupewrite;
    dupehashs[sum].time:=systime;
    dupewrite:=(dupewrite+1) MOD (HIGH(dupestrings)+1);
  ELSE
    DEC(i);
    LOOP
      IF dupestrings[i]=s THEN RETURN TRUE END;












END finddupe;

*/

static int32_t callchk(uint32_t * qpos, uint8_t * unset,
                char buf[], uint32_t buf_len, uint32_t * p,
                uint32_t * pssid, char withstar,
                char iscall0)
{
   uint32_t lit;
   uint32_t num;
   uint32_t j;
   uint32_t i0;
   char c;
   uint8_t u;
   *pssid = 0UL;
   for (u = udpgate4_gUNGATE;; u++) {
      j = 0UL;
      i0 = *p;
      while (ungates[u][j]==buf[i0]) {
         ++i0;
         ++j;
         if (ungates[u][j]==0) *unset |= (1U<<u);
      }
      if (u==udpgate4_gQ) break;
   } /* end for */
   if (iscall0) {
      /* we want a callsign */
      num = 0UL;
      lit = 0UL;
      c = buf[*p];
      if ((uint8_t)c>='A' && (uint8_t)c<='Z') ++lit;
      else if ((uint8_t)c<'0' || (uint8_t)c>'9') return -1L;
      ++*p;
      for (;;) {
         c = buf[*p];
         if ((uint8_t)c>='0' && (uint8_t)c<='9') ++num;
         else if ((uint8_t)c>='A' && (uint8_t)c<='Z') ++lit;
         else break;
         ++*p;
      }
      if ((lit<2UL || num==0UL) || num>2UL) return -1L;
      *pssid = *p;
      if (buf[*p]=='-') {
         ++*p;
         if (buf[*p]=='1') {
            ++*p;
            if ((uint8_t)buf[*p]>='0' && (uint8_t)buf[*p]<='5') ++*p;
         }
         else {
            if ((uint8_t)buf[*p]<'1' || (uint8_t)buf[*p]>'9') return -1L;
            ++*p;
         }
      }
   }
   else if ((buf[*p]=='q' && buf[*p+1UL]=='A') && (uint8_t)
                buf[*p+2UL]>='A') {
      /* qAx */
      *qpos = *p+2UL;
      *p += 3UL;
   }
   else {
      /* we skip any text */
      for (;;) {
         c = buf[*p];
         if (c=='-') *pssid = *p;
         if (((((uint8_t)c<=' ' || c=='>') || c=='*') || c==',') || c==':')
                 break;
         ++*p;
      }
   }
   if (*pssid==0UL) *pssid = *p;
   if (buf[*p]=='*' && withstar) ++*p;
   return 0L;
} /* end callchk() */


static void Iconstruct(char qtext[32], uint32_t * pins,
                char logcall[], uint32_t logcall_len, char buf[],
                uint32_t buf_len, uint32_t * p)
{
   uint32_t k;
   uint32_t j;
   uint32_t i0;
   if (*p<=4UL || buf[*p-2UL]!='I') return;
   /* ,CALL,I: */
   i0 = *p-4UL;
   while (buf[i0]!=',') {
      /* hop back call */
      if (i0==0UL) return;
      --i0;
   }
   k = i0; /* "," before call */
   ++i0;
   j = 0UL;
   while (j<=logcall_len-1 && logcall[j]) {
      if (logcall[j]!=buf[i0]) return;
      ++i0;
      ++j;
   }
   /* logcall = call,I */
   *pins = k;
   i0 = *p-1UL;
   *p = k+1UL; /* payload */
   for (;;) {
      buf[k] = buf[i0];
      if (i0>=buf_len-1 || buf[i0]==0) break;
      ++k;
      ++i0;
   }
   /* delete ,call,I */
   strncpy(qtext,",qAR,",32u); /* insert ,qAR,call */
   aprsstr_Append(qtext, 32ul, logcall, logcall_len);
/*
OE0AAA-9>T8SV40,WIDE1-1,DB0WGS,I:test
OE0AAA-9>T8SV40,WIDE1-1:test
OE0AAA-9>T8SV40,WIDE1-1,qAR,DB0WGS:test 
*/
} /* end Iconstruct() */


static int32_t AprsIs(char buf[], uint32_t buf_len,
                uint8_t datafilt, uint8_t msgfilt,
                const char logcall[], uint32_t logcall_len,
                uint32_t udpchan, char valid,
                struct POSCALL * poscall0)
{
   uint32_t qpos;
   uint32_t micedest;
   uint32_t payload;
   uint32_t ha;
   uint32_t ilen;
   uint32_t psum1;
   uint32_t psum0;
   uint32_t pssid;
   uint32_t pins;
   uint32_t len;
   uint32_t p;
   uint32_t i0;
   uint8_t hashh;
   uint8_t hashl;
   char qtext[32];
   char ungat;
   char postyp;
   uint8_t unset;
   FRAMEBUF com;
   uint32_t tmp;
   hashl = 0U;
   hashh = 0U;
   p = 0UL;
   pins = 0UL;
   pssid = 0UL; /* for savety */
   len = 0UL;
   poscall0->pos.lat = 0.0f;
   poscall0->pos.long0 = 0.0f;
   while ((len<buf_len-1 && buf[len]) && buf[len]!='\015') ++len;
   if (len==0UL || len+35UL>buf_len-1) return -2L;
   unset = 0U;
   qpos = 0UL;
   for (;;) {
      /* thirdparty loop */
      psum0 = p;
      if (callchk(&qpos, &unset, buf, buf_len, &p, &pssid, 0,
                udpchan || callsrc)) return -3L;
      /* src call */
      if (buf[p]!='>') return -4L;
      i0 = 0UL;
      while (i0<9UL && i0<p) {
         poscall0->call[i0] = buf[i0];
         ++i0;
      }
      poscall0->call[i0] = 0;
      ++p;
      micedest = p;
      if (callchk(&qpos, &unset, buf, buf_len, &p, &pssid, 0, 0)) return -3L;
      /* dest call */
      psum1 = pssid;
      while (buf[p]==',') {
         ++p;
         if (callchk(&qpos, &unset, buf, buf_len, &p, &pssid, 1, 0)) {
            return -3L;
         }
      }
      /* via calls */
      if (buf[p]!=':') return -4L;
      if (pins==0UL) pins = p;
      ++p;
      if (buf[p]!='}') break;
      ++p;
   }
   if (p>=len) return -2L;
   qtext[0U] = 0;
   if (qpos>0UL) {
      /* qA */
      if (X2C_CAP(buf[qpos])=='Z') return -5L;
      /* qAZ */
      if (X2C_CAP(buf[qpos])=='I') {
         aprsstr_Append(qtext, 32ul, ",", 2ul); /* qAI */
         aprsstr_Append(qtext, 32ul, servercall, 10ul);
      }
   }
   else if (udpchan) {
      aprsstr_Append(qtext, 32ul, ",qAU,", 6ul);
      aprsstr_Append(qtext, 32ul, servercall, 10ul);
   }
   else if (logcall[0UL]) {
      if (cmpfrom(buf, buf_len, 0UL, logcall, logcall_len)) {
         strncpy(qtext,",qAC,",32u);
      }
      else strncpy(qtext,",qAS,",32u);
      aprsstr_Append(qtext, 32ul, servercall, 10ul);
      Iconstruct(qtext, &pins, logcall, logcall_len, buf, buf_len, &p);
   }
   /*
   IO.WrStr(" <");FOR i:=0 TO Length(buf)-1 DO IO.WrHex(ORD(buf[i]),3) END;
   IO.WrStr("> "); IO.WrLn; IO.WrCard(pins, 10); IO.WrCard(p, 10); IO.WrLn;
   */
   payload = p;
   poscall0->typ0 = buf[p];
   if (buf[p]=='?') return -5L;
   if (udpchan || valid) {
      Getmsg(buf, buf_len, udpchan, (unset&msgfilt)==0U, &ungat);
      if (ungat) return -6L;
   }
   if ((unset&datafilt)!=0U) return -3L;
   tmp = psum1-1UL;
   i0 = psum0;
   if (i0<=tmp) for (;; i0++) {
      aprsstr_HashCh(buf[i0], &hashl, &hashh);
      if (i0==tmp) break;
   } /* end for */
   while ((p<len && buf[p]!='\015') && buf[p]!='\012') {
      aprsstr_HashCh(buf[p], &hashl, &hashh);
      ++p;
   }
   ha = (uint32_t)(uint8_t)(char)hashl+(uint32_t)(uint8_t)
                (char)hashh*256UL&65535UL;
   if (timehash[ha]+dupetime>systime) return -5L;
   /* a duplicate */
   if (udpchan || valid) timehash[ha] = systime;
   aprspos_GetPos(&poscall0->pos, &vspeed, &vcourse, &valt, &vsym, &vsymt,
                buf, buf_len, micedest, payload, com, 512ul, &postyp);
   if (udpchan==0UL && !valid) return -1L;
   buf[p] = '\015';
   ++p;
   buf[p] = '\012';
   ++p;
   buf[p] = 0;
   ++p;
   ilen = aprsstr_Length(qtext, 32ul);
   if (ilen>0UL) {
      while (p>pins) {
         --p;
         buf[p+ilen] = buf[p];
      }
      i0 = 0UL;
      while (i0<ilen) {
         buf[p] = qtext[i0];
         ++p;
         ++i0;
      }
   }
   return 0L;
} /* end AprsIs() */


static char getfix(uint32_t * p, char s[], uint32_t s_len,
                float * x, float mul)
{
   uint32_t i0;
   FRAMEBUF h;
   if (s[*p]=='/') {
      ++*p;
      i0 = 0UL;
      while (((uint8_t)s[*p]>' ' && s[*p]!='/') && i0<511UL) {
         h[i0] = s[*p];
         ++i0;
         ++*p;
      }
      h[i0] = 0;
      if (aprsstr_StrToFix(x, h, 512ul)) {
         /*
         IO.WrFixed(x, 4,10); IO.WrStr("   fix<"); IO.WrStr(h);
                IO.WrStrLn(">");
         */
         *x =  *x*mul;
         return 1;
      }
   }
   return 0;
} /* end getfix() */


static void getcalls(char s[], uint32_t s_len, uint32_t * p,
                MONCALL table[], uint32_t table_len)
{
   uint32_t j;
   uint32_t i0;
   ++*p;
   j = 0UL;
   while (s[*p]=='/') {
      ++*p;
      i0 = 0UL;
      while ((*p<=s_len-1 && (uint8_t)s[*p]>' ') && s[*p]!='/') {
         if (j<=table_len-1 && i0<=9UL) table[j][i0] = s[*p];
         ++*p;
         ++i0;
      }
      ++j;
   }
} /* end getcalls() */

typedef uint32_t CHSET0[4];

static CHSET0 _cnst0 = {0x00000000UL,0x00080000UL,0x00000000UL,0x00BBE200UL}
                ;

static void gettyps(char s[], uint32_t s_len, uint32_t * p,
                char t[], uint32_t t_len)
{
   uint32_t j;
   ++*p;
   j = 0UL;
   t[0UL] = 0;
   if (s[*p]=='/') {
      ++*p;
      while ((j<t_len-1 && *p<=s_len-1) && (uint8_t)s[*p]>' ') {
         if (X2C_INL((int32_t)(uint8_t)s[*p],128,
                _cnst0) && aprsstr_InStr(t, t_len, (char *) &s[*p],
                1u/1u)<0L) {
            t[j] = s[*p];
            t[j+1UL] = 0;
            ++j;
         }
         ++*p;
      }
   }
} /* end gettyps() */


static void GetFilters(struct FILTERS * filters, const char s[],
                uint32_t s_len, uint32_t p, pTCPSOCK pongto)
{
   char not;
   FRAMEBUF pongstr;
   skipblank(s, s_len, &p);
   if (cmpfrom(s, s_len, p, "filter", 7ul)) {
      p += 6UL;
      not = 0;
      filters->typ0 = 0; /* re-init if new filter comes */
      memset((char *)filters->viacalls,(char)0,80UL);
      memset((char *)filters->entrycalls,(char)0,80UL);
      memset((char *)filters->prefixes,(char)0,80UL);
      memset((char *)filters->typs,(char)0,13UL);
      memset((char *)filters->destcalls,(char)0,80UL);
      if (s[p]==' ') {
         skipblank(s, s_len, &p);
         while ((uint8_t)s[p]>=' ') {
            if (s[p]==' ') not = 0;
            skipblank(s, s_len, &p);
            if (s[p]=='m') {
               ++p;
               if (getfix(&p, s, s_len, &filters->radius, 1.0f)) {
                  filters->typ0 = 'm';
               }
               else return;
            }
            else if (s[p]=='r') {
               ++p;
               if (!getfix(&p, s, s_len, &filters->base.lat,
                1.7453292519444E-2f)) return;
               if (!getfix(&p, s, s_len, &filters->base.long0,
                1.7453292519444E-2f)) return;
               if (!getfix(&p, s, s_len, &filters->radius, 1.0f)) return;
               if (!aprspos_posvalid(filters->base)) return;
               filters->typ0 = 'r';
            }
            else if (s[p]=='a') {
               ++p;
               if (!getfix(&p, s, s_len, &filters->base.lat,
                1.7453292519444E-2f)) return;
               if (!getfix(&p, s, s_len, &filters->base.long0,
                1.7453292519444E-2f)) return;
               if (!getfix(&p, s, s_len, &filters->edge.lat,
                1.7453292519444E-2f)) return;
               if (!getfix(&p, s, s_len, &filters->edge.long0,
                1.7453292519444E-2f)) return;
               if (!aprspos_posvalid(filters->base)) return;
               if (!aprspos_posvalid(filters->edge)) return;
               filters->typ0 = 'a';
            }
            else if (s[p]=='d') {
               getcalls(s, s_len, &p, filters->viacalls, 8ul);
               filters->notvia = not;
            }
            else if (s[p]=='e') {
               getcalls(s, s_len, &p, filters->entrycalls, 8ul);
               filters->notentry = not;
            }
            else if (s[p]=='u') {
               getcalls(s, s_len, &p, filters->destcalls, 8ul);
               filters->notdestcall = not;
            }
            else if (s[p]=='p') {
               getcalls(s, s_len, &p, filters->prefixes, 8ul);
               filters->notprefix = not;
            }
            else if (s[p]=='t') {
               gettyps(s, s_len, &p, filters->typs, 13ul);
               filters->nottyps = not;
            }
            else if (s[p]=='-') {
               not = 1;
               ++p;
            }
            else return;
         }
      }
   }
   else if (pongto && cmpfrom(s, s_len, p, "ping ", 6ul)) {
      aprsstr_Assign(pongstr, 512ul, s, s_len);
      pongstr[p+1UL] = 'o'; /* ping -> pong */
      while (p<513UL && (uint8_t)pongstr[p]>=' ') {
         ++p;
      }
      pongstr[p] = '\015';
      pongstr[p+1UL] = '\012';
      pongstr[p+2UL] = 0;
      Sendtcp(pongto, pongstr);
      if (maxpongtime>0UL) {
         if (pongto->pongtime+maxpongtime>systime) pongto->pingout = 0;
         pongto->pongtime = systime+maxpongtime;
      }
   }
} /* end GetFilters() */


static void saypongout(pTCPSOCK pt)
{
   FRAMEBUF h;
   FRAMEBUF s;
   strncpy(s,"# ping timeout ",512u);
   aprsstr_IntToStr((int32_t)((systime+maxpongtime)-pt->pongtime), 0UL, h,
                512ul);
   aprsstr_Append(s, 512ul, h, 512ul);
   aprsstr_Append(s, 512ul, "s - data forwarding stopped\015\012", 30ul);
   Sendtcp(pt, s);
} /* end saypongout() */


static char Auth(const char mbuf0[], uint32_t mbuf_len,
                pTCPSOCK pu)
{
   uint32_t vport;
   uint32_t j;
   uint32_t i0;
   FRAMEBUF h1;
   FRAMEBUF h;
   pHEARD pmh;
   struct TCPSOCK * anonym;
   { /* with */
      struct TCPSOCK * anonym = pu;
      i0 = 0UL;
      while ((uint8_t)mbuf0[i0]<=' ') ++i0;
      if (mbuf0[i0]=='#') {
         GetFilters(&anonym->filters, mbuf0, mbuf_len, i0+1UL, 0);
         return 1;
      }
      if (mbuf0[i0]!='u') return 0;
      /* quick test */
      if (cmpfrom(mbuf0, mbuf_len, i0, "user ", 6ul)) {
         i0 += 5UL;
         skipblank(mbuf0, mbuf_len, &i0);
         j = 0UL;
         while (j<9UL && (uint8_t)mbuf0[i0]>' ') {
            anonym->user.call[j] = X2C_CAP(mbuf0[i0]);
            ++i0;
            ++j;
         }
         anonym->user.call[j] = 0;
         skipblank(mbuf0, mbuf_len, &i0);
         if (cmpfrom(mbuf0, mbuf_len, i0, "pass ", 6ul)) {
            i0 += 5UL;
            skipblank(mbuf0, mbuf_len, &i0);
            j = 0UL;
            while (((uint8_t)mbuf0[i0]>='0' && (uint8_t)mbuf0[i0]<='9')
                && j<32768UL) {
               j = (j*10UL+(uint32_t)(uint8_t)mbuf0[i0])-48UL;
               ++i0;
            }
            anonym->valid = aprsstr_Length(anonym->user.call,
                10ul)>=3UL && j==call2pass(anonym->user.call, 10ul);
            skipblank(mbuf0, mbuf_len, &i0);
         }
         if (cmpfrom(mbuf0, mbuf_len, i0, "vers ", 6ul)) {
            i0 += 5UL;
            skipblank(mbuf0, mbuf_len, &i0);
            j = 0UL;
            while ((uint8_t)mbuf0[i0]>=' ' && !cmpfrom(mbuf0, mbuf_len, i0,
                 "filter", 7ul)) {
               if (j<20UL) {
                  anonym->vers[j] = mbuf0[i0];
                  ++j;
               }
               ++i0;
            }
            while (j>0UL && anonym->vers[j-1UL]==' ') --j;
            anonym->vers[j] = 0;
         }
         GetFilters(&anonym->filters, mbuf0, mbuf_len, i0, 0);
         pmh = heardtcp;
         if ((!aprspos_posvalid(anonym->user.pos) && FindUserHeard(&pmh,
                anonym->user.call, &vport)>0UL) && pmh) {
            anonym->user.pos = pmh->position;
         }
         aprsstr_Assign(h, 512ul, "# logresp ", 11ul);
         aprsstr_Append(h, 512ul, anonym->user.call, 10ul);
         if (anonym->valid) aprsstr_Append(h, 512ul, " verified", 10ul);
         aprsstr_Append(h, 512ul, " server ", 9ul);
         aprsstr_Append(h, 512ul, servercall, 10ul);
         FiltToStr(anonym->filters, h1, 512ul);
         if (h1[0U]) {
            aprsstr_Append(h, 512ul, " filter ", 9ul);
            aprsstr_Append(h, 512ul, h1, 512ul);
         }
         aprsstr_Assign(h1, 512ul, h, 512ul);
         if (anonym->vers[0U]) {
            aprsstr_Append(h1, 512ul, " vers: ", 8ul);
            aprsstr_Append(h1, 512ul, anonym->vers, 21ul);
         }
         logline(1L, h1, 512ul);
         aprsstr_Append(h, 512ul, "\015\012", 3ul);
         Sendtcp(pu, h);
      }
   }
   return 1;
} /* end Auth() */


static void closetcp(pTCPSOCK * w, char fin)
{
   pWWWBUF pb;
   struct TCPSOCK * anonym;
   { /* with */
      struct TCPSOCK * anonym = *w;
      while (anonym->txbuf) {
         pb = anonym->txbuf;
         anonym->txbuf = anonym->txbuf->next;
         /*
         IO.WrStrLn("deallocw");
         */
         osic_free((char * *) &pb, sizeof(struct WWWBUF));
         if (!fin && anonym->txbuf) return;
      }
      if (anonym->fd>=0L) osic_Close(anonym->fd);
      anonym->fd = -1L;
   }
} /* end closetcp() */


static void sendwww(pTCPSOCK * w, WWWB b, int32_t len, char push)
{
   pWWWBUF pp;
   pWWWBUF pb;
   if (len==0L) return;
   /*
   IO.WrStrLn("allocw");
   */
   osic_alloc((char * *) &pb, sizeof(struct WWWBUF));
   if (pb==0) return;
   memcpy(pb->buf,b,1401u);
   pb->tlen = len;
   pb->push = push;
   pb->next = 0;
   if ((*w)->txbuf==0) (*w)->txbuf = pb;
   else {
      pp = (*w)->txbuf;
      while (pp->next) pp = pp->next;
      pp->next = pb;
   }
} /* end sendwww() */

#define udpgate4_RELURL "?reload="

#define udpgate4_SORTURL "?sort="

#define udpgate4_sortTIME "t"

#define udpgate4_sortCALL "c"

#define udpgate4_sortPORT "p"

#define udpgate4_sortHEARD "h"

#define udpgate4_sortDIST "d"

#define udpgate4_sortUP "u"

#define udpgate4_sortDOWN "d"


static void Appwww(pTCPSOCK * wsock, WWWB wbuf, const char s[],
                uint32_t s_len)
{
   uint32_t i0;
   uint32_t slen;
   uint32_t wlen;
   /*
       len:=Length(wbuf);
       IF len+Length(s)>=HIGH(wbuf) THEN
         sendwww(wsock, wbuf, len, FALSE);
         wbuf[0]:=0C;
       END;
       Append(wbuf, s);
   */
   wlen = aprsstr_Length(wbuf, 1401ul);
   slen = aprsstr_Length(s, s_len);
   i0 = 0UL;
   while (i0<slen) {
      if (wlen>=1400UL) {
         sendwww(wsock, wbuf, (int32_t)wlen, 0);
         wlen = 0UL;
      }
      wbuf[wlen] = s[i0];
      ++i0;
      ++wlen;
   }
   wbuf[wlen] = 0;
} /* end Appwww() */


static void AppCall(WWWB wbuf, pTCPSOCK * wsock, char c[],
                uint32_t c_len, char ungate, char center,
                const char link[], uint32_t link_len)
{
   char b[4096];
   uint32_t k;
   uint32_t j;
   uint32_t i0;
   X2C_PCOPY((void **)&c,c_len);
   Appwww(wsock, wbuf, "<TD style=text-align:", 22ul);
   if (center) Appwww(wsock, wbuf, "center>", 8ul);
   else Appwww(wsock, wbuf, "left>", 6ul);
   j = 0UL;
   i0 = 0UL;
   while ((uint8_t)link[i0]>=' ') {
      if (link[i0]=='$') {
         ++i0;
         if (link[i0]=='c') {
            k = 0UL;
            while ((k<=c_len-1 && c[k]) && j<4095UL) {
               b[j] = c[k];
               ++j;
               ++k;
            }
         }
         else if (link[i0]=='$') {
            b[j] = '$';
            if (j<4095UL) ++j;
         }
      }
      else {
         b[j] = link[i0];
         if (j<4095UL) ++j;
      }
      ++i0;
   }
   b[j] = 0;
   if (b[0U]) {
      Appwww(wsock, wbuf, "<a href=", 9ul);
      Appwww(wsock, wbuf, b, 4096ul);
      Appwww(wsock, wbuf, ">", 2ul);
   }
   if (ungate) {
      Appwww(wsock, wbuf, "#", 2ul);
   }
   Appwww(wsock, wbuf, c, c_len);
   if (b[0U]) Appwww(wsock, wbuf, "</a>", 5ul);
   Appwww(wsock, wbuf, "</TD>", 6ul);
   X2C_PFREE(c);
} /* end AppCall() */


static void AppTime(WWWB wbuf, pTCPSOCK * wsock, uint32_t t,
                char tab)
{
   char h1[256];
   if (tab) Appwww(wsock, wbuf, "<td>", 5ul);
   if (systime<t || t==0UL) t = 0UL;
   else t = systime-t;
   aprsstr_TimeToStr(t, h1, 256ul);
   Appwww(wsock, wbuf, h1, 256ul);
   if (tab) Appwww(wsock, wbuf, "</td>", 6ul);
} /* end AppTime() */


static void AppMinSec(WWWB wbuf, pTCPSOCK * wsock, uint32_t t)
{
   char h1[21];
   /*
       Appwww("<td>");
   */
   if (systime<t || t==0UL) t = 0UL;
   else t = systime-t;
   if (t/3600UL>0UL) {
      aprsstr_IntToStr((int32_t)(t/3600UL), 1UL, h1, 21ul);
      Appwww(wsock, wbuf, h1, 21ul);
      Appwww(wsock, wbuf, "h", 2ul);
      t = t%3600UL;
   }
   if (t/60UL>0UL) {
      aprsstr_IntToStr((int32_t)(t/60UL), 1UL, h1, 21ul);
      Appwww(wsock, wbuf, h1, 21ul);
      Appwww(wsock, wbuf, "m", 2ul);
      t = t%60UL;
   }
   aprsstr_IntToStr((int32_t)t, 1UL, h1, 21ul);
   if (h1[1U]==0) {
      Appwww(wsock, wbuf, "&nbsp;", 7ul);
   }
   Appwww(wsock, wbuf, h1, 21ul);
   Appwww(wsock, wbuf, "s", 2ul);
/*
    Appwww("</td>");
*/
} /* end AppMinSec() */


static void AppInt(WWWB wbuf, pTCPSOCK * wsock, int32_t n)
{
   char h1[256];
   Appwww(wsock, wbuf, "<td>", 5ul);
   aprsstr_IntToStr(n, 1UL, h1, 256ul);
   Appwww(wsock, wbuf, h1, 256ul);
   Appwww(wsock, wbuf, "</td>", 6ul);
} /* end AppInt() */


static void AppTxt(WWWB wbuf, pTCPSOCK * wsock, const char style[],
                uint32_t style_len, const char s[], uint32_t s_len)
{
   char c;
   int32_t i0;
   char h1[256];
   int32_t tmp;
   Appwww(wsock, wbuf, style, style_len);
   tmp = (int32_t)aprsstr_Length(s, s_len)-1L;
   i0 = 0L;
   if (i0<=tmp) for (;; i0++) {
      c = s[i0];
      switch ((unsigned)c) {
      case ' ':
      case '.':
      case ',':
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
      case 'A':
      case 'B':
      case 'C':
      case 'D':
      case 'E':
      case 'F':
      case 'G':
      case 'H':
      case 'I':
      case 'J':
      case 'K':
      case 'L':
      case 'M':
      case 'N':
      case 'O':
      case 'P':
      case 'Q':
      case 'R':
      case 'S':
      case 'T':
      case 'U':
      case 'V':
      case 'W':
      case 'X':
      case 'Y':
      case 'Z':
      case 'a':
      case 'b':
      case 'c':
      case 'd':
      case 'e':
      case 'f':
      case 'g':
      case 'h':
      case 'i':
      case 'j':
      case 'k':
      case 'l':
      case 'm':
      case 'n':
      case 'o':
      case 'p':
      case 'q':
      case 'r':
      case 's':
      case 't':
      case 'u':
      case 'v':
      case 'w':
      case 'x':
      case 'y':
      case 'z':
         Appwww(wsock, wbuf, (char *) &c, 1u/1u);
         break;
      case 0:
      case '\001':
      case '\002':
      case '\003':
      case '\004':
      case '\005':
      case '\006':
      case '\007':
      case '\010':
      case '\011':
      case '\012':
      case '\013':
      case '\014':
      case '\015':
      case '\016':
      case '\017':
      case '\020':
      case '\021':
      case '\022':
      case '\023':
      case '\024':
      case '\025':
      case '\026':
      case '\027':
      case '\030':
      case '\031':
      case '\032':
      case '\033':
      case '\034':
      case '\035':
      case '\036':
      case '\037':
         break;
      default:;
         Appwww(wsock, wbuf, "&#", 3ul);
         aprsstr_IntToStr((int32_t)(uint32_t)(uint8_t)c, 1UL, h1,
                256ul);
         Appwww(wsock, wbuf, h1, 256ul);
         Appwww(wsock, wbuf, ";", 2ul);
         break;
      } /* end switch */
      if (i0==tmp) break;
   } /* end for */
   Appwww(wsock, wbuf, "</td>", 6ul);
} /* end AppTxt() */


static char hex(uint32_t n)
{
   if (n>9UL) n += 7UL;
   return (char)(n+48UL);
} /* end hex() */


static void setcol(char h1[256], uint32_t c, uint32_t n)
{
   if (n>100UL) n = 100UL;
   n += 155UL;
   h1[c] = hex(n/16UL);
   h1[c+1UL] = hex(n&15UL);
} /* end setcol() */


static void redgreen(char h1[256], int32_t c)
{
   if (c<0L) c = 0L;
   if (c<100L) {
      setcol(h1, 0UL, (uint32_t)c);
      setcol(h1, 2UL, 100UL);
   }
   else {
      if (c>200L) c = 200L;
      setcol(h1, 0UL, 100UL);
      setcol(h1, 2UL, (uint32_t)(200L-c));
   }
} /* end redgreen() */


static void green(char h1[256], uint32_t cnt, uint32_t maxt,
                char driving)
{
   uint32_t t;
   t = systime-uptime;
   if (t>maxt) t = maxt;
   if (driving) cnt = cnt*2000UL;
   else cnt = cnt*20000UL;
   redgreen(h1, (int32_t)(cnt/(t+300UL)));
} /* end green() */


static void wcard64(WWWB wbuf, pTCPSOCK * wsock, uint32_t hi,
                uint32_t lo)
{
   char h1[16];
   if (hi>0UL) {
      aprsstr_CardToStr(lo/1024UL+hi*4194304UL, 1UL, h1, 16ul);
      aprsstr_Append(h1, 16ul, "k", 2ul);
   }
   else aprsstr_CardToStr(lo, 1UL, h1, 16ul);
   Appwww(wsock, wbuf, "<TD style=\"text-align:right\">", 30ul);
   Appwww(wsock, wbuf, h1, 16ul);
   Appwww(wsock, wbuf, "</TD>", 6ul);
} /* end wcard64() */


static void wint(WWWB wbuf, pTCPSOCK * wsock, int32_t n)
{
   char h1[16];
   aprsstr_IntToStr(n, 1UL, h1, 16ul);
   Appwww(wsock, wbuf, "<TD style=\"text-align:right\">", 30ul);
   Appwww(wsock, wbuf, h1, 16ul);
   Appwww(wsock, wbuf, "</TD>", 6ul);
} /* end wint() */


static void wintint(WWWB wbuf, pTCPSOCK * wsock, int32_t n, int32_t k)
{
   char h1[16];
   aprsstr_IntToStr(n, 1UL, h1, 16ul);
   Appwww(wsock, wbuf, "<TD style=\"text-align:right\">", 30ul);
   Appwww(wsock, wbuf, h1, 16ul);
   Appwww(wsock, wbuf, "<BR>", 5ul);
   aprsstr_IntToStr(k, 1UL, h1, 16ul);
   Appwww(wsock, wbuf, h1, 16ul);
   Appwww(wsock, wbuf, "</TD>", 6ul);
} /* end wintint() */


static char openfile(char fn[], uint32_t fn_len, int32_t * fd,
                 int32_t * flen)
{
   char h[4097];
   char hh[2];
   /*
         s:stat_t;
   */
   uint32_t i0;
   char openfile_ret;
   X2C_PCOPY((void **)&fn,fn_len);
   if (fn[0UL]=='/') {
      openfile_ret = 0;
      goto label;
   }
   i0 = 0UL;
   for (;;) {
      if (i0+2UL>=fn_len-1 || fn[i0]==0) break;
      if ((uint8_t)fn[i0]<=' ' || (uint8_t)fn[i0]>=(uint8_t)'\200') {
         openfile_ret = 0;
         goto label;
      }
      if ((fn[i0]=='.' && fn[i0+1UL]=='.') && fn[i0+2UL]=='/') {
         openfile_ret = 0;
         goto label;
      }
      ++i0;
   }
   aprsstr_Assign(h, 4097ul, wwwdir, 1024ul);
   aprsstr_Append(h, 4097ul, fn, fn_len);
   *fd = osi_OpenRead(h, 4097ul);
   if (*fd<0L || osi_RdBin(*fd, (char *)hh, 2u/1u, 1UL)!=1L) {
      openfile_ret = 0;
      goto label;
   }
   /* test read if regular file */
   osic_Seek(*fd, 0UL);
   /*
       fstat(fd, s);
       IF CAST(BITSET, s.st_mode)*CAST(BITSET,170000B)<>CAST(BITSET,
                100000B) THEN RETURN FALSE END;
   */
   *flen = osic_Size(*fd);
   if (*flen<=0L || *flen>=wwwsizelimit) {
      openfile_ret = 0;
      goto label;
   }
   openfile_ret = 1;
   label:;
   X2C_PFREE(fn);
   return openfile_ret;
} /* end openfile() */


static void appsorturl(WWWB wbuf, pTCPSOCK * wsock, const char sortby[],
                uint32_t sortby_len)
{
   char h[3];
   if (sortby[0UL]) {
      Appwww(wsock, wbuf, "?sort=", 7ul);
      h[0U] = sortby[0UL];
      h[1U] = sortby[1UL];
      h[2U] = 0;
      Appwww(wsock, wbuf, h, 3ul);
   }
} /* end appsorturl() */


static void appreloadurl(WWWB wbuf, pTCPSOCK * wsock, uint32_t time0)
{
   char h[32];
   if (time0) {
      Appwww(wsock, wbuf, "?reload=", 9ul);
      aprsstr_IntToStr((int32_t)time0, 1UL, h, 32ul);
      Appwww(wsock, wbuf, h, 32ul);
   }
} /* end appreloadurl() */


static void klick(WWWB wbuf, pTCPSOCK * wsock, char path[],
                uint32_t path_len, char text[], uint32_t text_len,
                char sort)
{
   char s[2];
   X2C_PCOPY((void **)&path,path_len);
   X2C_PCOPY((void **)&text,text_len);
   aprsstr_Assign(s, 2ul, (*wsock)->sortby, 2ul);
   if (sort) {
      if (s[0U]==sort) {
         if (s[1U]!='u') s[1U] = 'u';
         else if (s[1U]!='d') s[1U] = 'd';
      }
      else {
         s[0U] = sort;
         s[1U] = 'u';
      }
   }
   Appwww(wsock, wbuf, "<a href=\"", 10ul);
   Appwww(wsock, wbuf, path, path_len);
   appsorturl(wbuf, wsock, s, 2ul);
   appreloadurl(wbuf, wsock, (*wsock)->reload);
   Appwww(wsock, wbuf, "\">", 3ul);
   Appwww(wsock, wbuf, text, text_len);
   Appwww(wsock, wbuf, "</a> ", 6ul);
   X2C_PFREE(path);
   X2C_PFREE(text);
} /* end klick() */


static char iconf(char sym, char symt, char fn[],
                uint32_t fn_len, char * overlay)
{
   char h[256];
   uint32_t i0;
   i0 = (uint32_t)(uint8_t)sym;
   if (i0<33UL || i0>127UL) return 0;
   aprsstr_Assign(fn, fn_len, "icon/", 6ul);
   aprsstr_IntToStr((int32_t)(i0+67UL), 3UL, h, 256ul);
   *overlay = 0;
   switch ((unsigned)symt) {
   case '0':
   case '1':
   case '2':
   case '3':
   case '4':
   case '5':
   case '6':
   case '7':
   case '8':
   case '9':
   case 'A':
   case 'B':
   case 'C':
   case 'D':
   case 'E':
   case 'F':
   case 'G':
   case 'H':
   case 'I':
   case 'J':
   case 'K':
   case 'L':
   case 'M':
   case 'N':
   case 'O':
   case 'P':
   case 'Q':
   case 'R':
   case 'S':
   case 'T':
   case 'U':
   case 'V':
   case 'W':
   case 'X':
   case 'Y':
   case 'Z':
      h[0U] = '2';
      *overlay = symt;
      break;
   case '\\':
      h[0U] = '2';
      break;
   case '/':
      break;
   default:;
      return 0;
   } /* end switch */
   aprsstr_Append(fn, fn_len, h, 256ul);
   aprsstr_Append(fn, fn_len, ".gif", 5ul);
   aprsstr_Assign(h, 256ul, wwwdir, 1024ul);
   aprsstr_Append(h, 256ul, fn, fn_len);
   return osi_Exists(h, 256ul);
} /* end iconf() */


static void sortindex(pHEARD ph, uint32_t maxtime, const char sortby[],
                 uint32_t sortby_len, char * withqual)
/* make LONGREAL of sort key */
{
   char homevalid;
   uint32_t i0;
   char c;
   char s;
   double xs;
   *withqual = 0;
   xs = 0.0;
   homevalid = aprspos_posvalid(home);
   s = sortby[0UL];
   while (ph) {
      switch ((unsigned)s) {
      case 't': /* sort by time as it is */
         xs = xs+1.0;
         break;
      case 'c': /* sort by call */
         xs = 0.0;
         for (i0 = 0UL; i0<=9UL; i0++) {
            c = ph->call[i0];
            xs = xs*128.0+(double)(float)(uint32_t)(uint8_t)c;
         } /* end for */
         break;
      case 'p': /* sort by port */
         xs = (double)(float)ph->fromrx;
         break;
      case 'h': /* sort by count */
         xs = (double)(float)MHcount(ph, maxtime);
         break;
      case 'd': /* sort by km */
         if (homevalid && aprspos_posvalid(ph->position)) {
            xs = (double)aprspos_distance(home, ph->position);
         }
         else xs = 0.0;
         break;
      } /* end switch */
      if (sortby[1UL]=='d') ph->sortval = -xs;
      else ph->sortval = xs;
      if ((ph->txd || ph->level) || ph->quali) *withqual = 1;
      ph = ph->next;
   }
} /* end sortindex() */


static void getlinkfile(char b[], uint32_t b_len, const char fn[],
                uint32_t fn_len)
{
   int32_t len;
   int32_t fd;
   aprsstr_Assign(b, b_len, wwwdir, 1024ul);
   aprsstr_Append(b, b_len, fn, fn_len);
   fd = osi_OpenRead(b, b_len);
   b[0UL] = 0;
   if (fd>=0L) {
      len = osi_RdBin(fd, (char *)b, (b_len)/1u, b_len-1UL);
      if (len<0L) len = 0L;
      b[len] = 0;
      osic_Close(fd);
   }
} /* end getlinkfile() */


static void apppos(WWWB wbuf, pTCPSOCK * wsock, struct aprspos_POSITION pos,
                char withloc)
{
   char h[32];
   if (aprspos_posvalid(pos)) {
      aprsstr_FixToStr(X2C_DIVR(pos.lat,1.7453292519444E-2f), 5UL, h, 32ul);
      Appwww(wsock, wbuf, " ", 2ul);
      Appwww(wsock, wbuf, h, 32ul);
      aprsstr_FixToStr(X2C_DIVR(pos.long0,1.7453292519444E-2f), 5UL, h,
                32ul);
      Appwww(wsock, wbuf, "/", 2ul);
      Appwww(wsock, wbuf, h, 32ul);
      if (withloc) {
         postoloc(h, 32ul, pos);
         Appwww(wsock, wbuf, " ", 2ul);
         Appwww(wsock, wbuf, h, 32ul);
      }
   }
   else Appwww(wsock, wbuf, " (NoPos)", 9ul);
} /* end apppos() */


static void showmh(WWWB wbuf, pTCPSOCK * wsock, char h1[256], pHEARD ph0,
                 char dir, char net, uint32_t maxtime,
                char title0[], uint32_t title_len,
                const char sortby[], uint32_t sortby_len)
{
   char withqual;
   char withicon;
   char withport;
   uint32_t ci;
   char hch;
   pHEARD phs;
   pHEARD ph;
   double xs;
   char callink[1024];
   X2C_PCOPY((void **)&title0,title_len);
   getlinkfile(callink, 1024ul, "calllink.txt", 13ul);
   sortindex(ph0, maxtime, sortby, sortby_len, &withqual);
   withport = udpsocks && udpsocks->next;
   aprsstr_Assign(h1, 256ul, wwwdir, 1024ul);
   aprsstr_Append(h1, 256ul, "icon", 5ul);
   withicon = osi_Exists(h1, 256ul);
   Appwww(wsock, wbuf, "<table id=mheard border=0 align=center CELLPADDING=3 \
CELLSPACING=1 BGCOLOR=#FFFFFF><tr class=tab-mh-titel BGCOLOR=#A9EEFF><th cols\
pan=", 135ul);
   aprsstr_IntToStr((int32_t)(6UL+(uint32_t)withicon+(uint32_t)
                withport+(uint32_t)(dir && withqual)*3UL+(uint32_t)net),
                1UL, h1, 256ul);
   Appwww(wsock, wbuf, h1, 256ul);
   Appwww(wsock, wbuf, ">", 2ul);
   Appwww(wsock, wbuf, title0, title_len);
   if (maxtime%3600UL==0UL) {
      aprsstr_IntToStr((int32_t)(maxtime/3600UL), 1UL, h1, 256ul);
      aprsstr_Append(h1, 256ul, "h", 2ul);
   }
   else {
      aprsstr_IntToStr((int32_t)(maxtime/60UL), 1UL, h1, 256ul);
      aprsstr_Append(h1, 256ul, "min", 4ul);
   }
   Appwww(wsock, wbuf, h1, 256ul);
   /*    Appwww('</th></tr><tr class=tab-mh-head BGCOLOR=#E0C080><th>Call</th>'
                ); */
   Appwww(wsock, wbuf, "</th></tr><tr class=tab-mh-head BGCOLOR=#E0C080><th>",
                 53ul);
   klick(wbuf, wsock, "mh", 3ul, "Call", 5ul, 'c');
   Appwww(wsock, wbuf, "</th>", 6ul);
   if (withicon) Appwww(wsock, wbuf, "<th>Icon</th>", 14ul);
   /*    IF withport THEN Appwww('<th>Port</th>') END; */
   if (withport) {
      Appwww(wsock, wbuf, "<th>", 5ul);
      klick(wbuf, wsock, "mh", 3ul, "Port", 5ul, 'p');
      Appwww(wsock, wbuf, "</th>", 6ul);
   }
   /*    Appwww('<th>Last Heard</th><th>Pack</th><th>QRB km</th><th>Data</th><th>Path</th></tr>'
                ); */
   Appwww(wsock, wbuf, "<th>", 5ul);
   klick(wbuf, wsock, "mh", 3ul, "Last Heard", 11ul, 't');
   if (dir && withqual) {
      Appwww(wsock, wbuf, "</th><th>Txd</th><th>Lev</th><th>q&#37;", 40ul);
   }
   if (net) Appwww(wsock, wbuf, "</th><th>Position", 18ul);
   Appwww(wsock, wbuf, "</th><th>", 10ul);
   klick(wbuf, wsock, "mh", 3ul, "Pack", 5ul, 'h');
   Appwww(wsock, wbuf, "</th><th>", 10ul);
   klick(wbuf, wsock, "mh", 3ul, "QRB km", 7ul, 'd');
   Appwww(wsock, wbuf, "</th><th>Data</th><th>Path</th></tr>", 37ul);
   for (;;) {
      phs = ph0;
      xs = X2C_max_longreal;
      ph = 0;
      while (phs) {
         if (phs->sortval<xs) {
            xs = phs->sortval;
            ph = phs;
         }
         phs = phs->next;
      }
      if (ph==0) break;
      ph->sortval = X2C_max_longreal;
      if (ph->time0+maxtime>systime) {
         ci = MHcount(ph, maxtime);
         Appwww(wsock, wbuf, "<tr style=\"background-color:#90FF90; text-alig\
n:right\">", 56ul);
         AppCall(wbuf, wsock, ph->call, 10ul, ph->ungate, 0, callink,
                1024ul);
         if (withicon) {
            Appwww(wsock, wbuf, "<td", 4ul);
            if (iconf(ph->sym, ph->symt, h1, 256ul, &hch)) {
               Appwww(wsock, wbuf, " style=\"background-image:url(/", 31ul);
               Appwww(wsock, wbuf, h1, 256ul);
               Appwww(wsock, wbuf, "); background-repeat:no-repeat;background\
-position:center center;", 66ul);
               if (hch) {
                  Appwww(wsock, wbuf, "text-align:center\">", 20ul);
                  Appwww(wsock, wbuf, (char *) &hch, 1u/1u);
               }
               else Appwww(wsock, wbuf, "\">", 3ul);
            }
            else Appwww(wsock, wbuf, ">", 2ul);
            Appwww(wsock, wbuf, "</td>", 6ul);
         }
         if (withport) {
            Appwww(wsock, wbuf, "<td>", 5ul);
            getportname(ph->fromrx, h1, 256ul);
            Appwww(wsock, wbuf, h1, 256ul);
            Appwww(wsock, wbuf, "</td>", 6ul);
         }
         Appwww(wsock, wbuf, "<td>", 5ul);
         AppMinSec(wbuf, wsock, ph->time0);
         Appwww(wsock, wbuf, "</td>", 6ul);
         if (dir && withqual) {
            /* txdel */
            Appwww(wsock, wbuf, "<td style=\"background-color:#", 30ul);
            strncpy(h1,"80FF80",256u);
            redgreen(h1, (int32_t)ph->txd-50L);
            Appwww(wsock, wbuf, h1, 256ul);
            Appwww(wsock, wbuf, "\">", 3ul);
            if (ph->txd) {
               aprsstr_IntToStr((int32_t)ph->txd, 1UL, h1, 256ul);
               Appwww(wsock, wbuf, h1, 256ul);
            }
            Appwww(wsock, wbuf, "</td>", 6ul);
            /* level */
            Appwww(wsock, wbuf, "<td style=\"background-color:#", 30ul);
            strncpy(h1,"80FF80",256u);
            if (ph->level) {
               redgreen(h1, ((int32_t)Min((uint32_t)(uint8_t)abs(ph->level+15),
                 15UL)-5L)*10L);
            }
            Appwww(wsock, wbuf, h1, 256ul);
            Appwww(wsock, wbuf, "\">", 3ul);
            if (ph->level) {
               aprsstr_IntToStr((int32_t)ph->level, 1UL, h1, 256ul);
               Appwww(wsock, wbuf, h1, 256ul);
            }
            Appwww(wsock, wbuf, "</td>", 6ul);
            /*quality */
            Appwww(wsock, wbuf, "<td>", 5ul);
            if (ph->quali) {
               aprsstr_IntToStr((int32_t)ph->quali, 1UL, h1, 256ul);
               Appwww(wsock, wbuf, h1, 256ul);
            }
            Appwww(wsock, wbuf, "</td>", 6ul);
         }
         if (net) {
            Appwww(wsock, wbuf, "<td>", 5ul);
            apppos(wbuf, wsock, ph->position, 0);
            Appwww(wsock, wbuf, "</td>", 6ul);
         }
         Appwww(wsock, wbuf, "<td style=\"background-color:#", 30ul);
         strncpy(h1,"80FF80",256u);
         green(h1, ci, maxtime, ph->datatyp=='S');
         Appwww(wsock, wbuf, h1, 256ul);
         Appwww(wsock, wbuf, "\">", 3ul);
         aprsstr_IntToStr((int32_t)ci, 1UL, h1, 256ul);
         Appwww(wsock, wbuf, h1, 256ul);
         Appwww(wsock, wbuf, "</td>", 6ul);
         h1[0U] = 0;
         if (aprspos_posvalid(ph->position) && aprspos_posvalid(home)) {
            aprsstr_FixToStr(aprspos_distance(home, ph->position)+0.05f, 2UL,
                 h1, 256ul);
         }
         Appwww(wsock, wbuf, "<td>", 5ul);
         Appwww(wsock, wbuf, h1, 256ul);
         Appwww(wsock, wbuf, "</td>", 6ul);
         Appwww(wsock, wbuf, "<td>", 5ul);
         if (ph->datatyp=='C') {
            aprsstr_FixToStr(ph->data, 2UL, h1, 256ul);
            aprsstr_Append(h1, 256ul, "\260C", 3ul);
         }
         else if (ph->datatyp=='S') {
            aprsstr_FixToStr(ph->data, 0UL, h1, 256ul);
            aprsstr_Append(h1, 256ul, "kmh", 4ul);
         }
         else h1[0] = 0;
         Appwww(wsock, wbuf, h1, 256ul);
         Appwww(wsock, wbuf, "</td>", 6ul);
         AppTxt(wbuf, wsock, "<td style=text-align:left>", 27ul, ph->head,
                41ul);
         Appwww(wsock, wbuf, "</tr>\015\012", 8ul);
      }
   }
   Appwww(wsock, wbuf, "</table>&nbsp;", 15ul);
   X2C_PFREE(title0);
} /* end showmh() */


static void getreload(char s[], uint32_t s_len, uint32_t * r)
{
   int32_t len;
   int32_t j;
   int32_t i0;
   uint32_t n;
   n = 0UL;
   i0 = aprsstr_InStr(s, s_len, "?reload=", 9ul);
   if (i0>=0L) {
      len = (int32_t)aprsstr_Length(s, s_len);
      j = i0;
      i0 += (int32_t)aprsstr_Length("?reload=", 9ul);
      while ((i0<len && (uint8_t)s[i0]>='0') && (uint8_t)s[i0]<='9') {
         n = (n*10UL+(uint32_t)(uint8_t)s[i0])-48UL;
         ++i0;
      }
      while (i0<=len) {
         s[j] = s[i0];
         ++i0;
         ++j;
      }
   }
   if (n>0UL) *r = n;
} /* end getreload() */


static void getmh(char s[], uint32_t s_len, char sortby[],
                uint32_t sortby_len)
{
   int32_t len;
   int32_t j;
   int32_t i0;
   sortby[0UL] = 0;
   sortby[1UL] = 0;
   i0 = aprsstr_InStr(s, s_len, "?sort=", 7ul);
   if (i0>=0L) {
      len = (int32_t)aprsstr_Length(s, s_len);
      j = i0;
      i0 += (int32_t)aprsstr_Length("?sort=", 7ul);
      sortby[0UL] = s[i0];
      ++i0;
      sortby[1UL] = s[i0];
      ++i0;
      while (i0<=len) {
         s[j] = s[i0];
         ++i0;
         ++j;
      }
   }
} /* end getmh() */


static void appreload(WWWB wbuf, pTCPSOCK * wsock, uint32_t time0)
{
   char h[32];
   if (time0) {
      Appwww(wsock, wbuf, "<meta http-equiv=\"refresh\" content=\"", 37ul);
      aprsstr_IntToStr((int32_t)(time0*60UL), 1UL, h, 32ul);
      Appwww(wsock, wbuf, h, 32ul);
      Appwww(wsock, wbuf, "\">", 3ul);
   }
} /* end appreload() */


static void reloadklick(WWWB wbuf, pTCPSOCK * wsock)
{
   char h1[16];
   /*
       Appwww('<form method="get">reload min:<input type="text" value="');
   */
   Appwww(wsock, wbuf, "Reload (min):<input type=\"text\" value=\"", 40ul);
   aprsstr_IntToStr((int32_t)(*wsock)->reload, 1UL, h1, 16ul);
   Appwww(wsock, wbuf, h1, 16ul);
   Appwww(wsock, wbuf, "\" name=\"reload\" size=\"4\" maxlength=\"3\"><input \
type=\"submit\" value=\"set reload time\"/></form>", 93ul);
} /* end reloadklick() */


static void klicks(WWWB wbuf, pTCPSOCK * wsock)
{
   /*
       Appwww('<div style="text-align:center" class="nav">');
   */
   Appwww(wsock, wbuf, "<div style=\"text-align:center\" class=\"nav\"><form \
method=\"get\">", 63ul);
   klick(wbuf, wsock, "/", 2ul, "CONNECTS", 9ul, 0);
   klick(wbuf, wsock, "mh", 3ul, "HEARD", 6ul, 0);
   if (maxmsg>0UL) klick(wbuf, wsock, "msg", 4ul, "MSGRELAY", 9ul, 0);
   klick(wbuf, wsock, "info.html", 10ul, "INFO", 5ul, 0);
   reloadklick(wbuf, wsock);
   Appwww(wsock, wbuf, "</div>", 7ul);
} /* end klicks() */


static void conthead(pTCPSOCK * wsock, WWWB wbuf)
{
   strncpy(wbuf,"HTTP/1.0 200 OK\015\012Content-Type: text/html; charset=iso-\
8859-1\015\012\015\012",1401u);
   sendwww(wsock, wbuf, (int32_t)aprsstr_Length(wbuf, 1401ul), 1);
   strncpy(wbuf,"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//\
EN\"\015\012\"http://www.w3.org/TR/html4/loose.dtd\">\015\012<HTML><HEAD>",
                1401u);
   appreload(wbuf, wsock, (*wsock)->reload);
} /* end conthead() */


static void title(WWWB wbuf, pTCPSOCK * wsock, uint32_t * cnt)
{
   char h[32];
   ++*cnt;
   Appwww(wsock, wbuf, "<TITLE>", 8ul);
   Appwww(wsock, wbuf, viacall, 10ul);
   Appwww(wsock, wbuf, " Message Relay</TITLE><link href=\"style.css\" rel=\"\
stylesheet\" type=\"text/css\"></HEAD>\015\012<BODY><div style=\"text-align:c\
enter\"><H3>\015\012", 131ul);
   if (viacall[0U]) {
      Appwww(wsock, wbuf, " MsgCall ", 10ul);
      Appwww(wsock, wbuf, viacall, 10ul);
   }
   apppos(wbuf, wsock, home, 1);
   Appwww(wsock, wbuf, " [udpgate(c) 0.63] http#", 25ul);
   aprsstr_IntToStr((int32_t)*cnt, 1UL, h, 32ul);
   Appwww(wsock, wbuf, h, 32ul);
   Appwww(wsock, wbuf, " Uptime ", 9ul);
   AppTime(wbuf, wsock, uptime, 0);
   Appwww(wsock, wbuf, "</H3></div>\015\012", 14ul);
} /* end title() */


static void Www(pTCPSOCK wsock)
{
   WWWB wbuf;
   char h1[256];
   int32_t res0;
   int32_t flen;
   int32_t fdw;
   uint32_t ut;
   uint32_t tt;
   pTCPSOCK ss;
   pUDPSOCK us;
   uint32_t i0;
   char serverlink[1024];
   pMESSAGE mp;
   uint8_t tp;
   char hok;
   struct TCPSOCK * anonym;
   struct TCPSOCK * anonym0;
   struct _0 * anonym1;
   struct TCPSOCK * anonym2;
   /* Www */
   res0 = readsock(wsock->fd, wbuf, 1401L);
   i0 = 0UL;
   for (;;) {
      if ((int32_t)i0>=res0) {
         if (res0<=0L) {
            osic_Close(wsock->fd);
            wsock->fd = -1L;
         }
         return;
      }
      { /* with */
         struct TCPSOCK * anonym = wsock;
         anonym->get[anonym->wwwst] = wbuf[i0];
         if (anonym->wwwst<255UL) ++anonym->wwwst;
         else {
            anonym->get[anonym->wwwst-4UL] = anonym->get[anonym->wwwst-3UL];
            anonym->get[anonym->wwwst-3UL] = anonym->get[anonym->wwwst-2UL];
            anonym->get[anonym->wwwst-2UL] = anonym->get[anonym->wwwst-1UL];
            anonym->get[anonym->wwwst-1UL] = anonym->get[anonym->wwwst];
         }
         anonym->get[anonym->wwwst] = 0;
         /*
         IO.WrCard(wwwst, 10); FOR tt:=0 TO wwwst DO IO.WrHex(ORD(get[tt]), 3);
                 END; IO.WrLn;
         */
         if (((((anonym->wwwst>=4UL && anonym->get[anonym->wwwst-4UL]
                =='\015') && anonym->get[anonym->wwwst-3UL]=='\012')
                && anonym->get[anonym->wwwst-2UL]=='\015')
                && anonym->get[anonym->wwwst-1UL]=='\012')
                && cmpfrom(anonym->get, 256ul, 0UL, "GET /", 6ul)) break;
         ++i0;
      }
   }
   { /* with */
      struct TCPSOCK * anonym0 = wsock;
      i0 = 5UL;
      while (i0<=255UL && (uint8_t)anonym0->get[i0]>' ') {
         anonym0->get[i0-5UL] = anonym0->get[i0];
         ++i0;
      }
      anonym0->get[i0-5UL] = 0;
      getreload(anonym0->get, 256ul, &anonym0->reload);
      getmh(anonym0->get, 256ul, anonym0->sortby, 2ul);
   }
   if (wsock->get[0U]==0) {
      ++httpcount;
      conthead(&wsock, wbuf);
      Appwww(&wsock, wbuf, "<TITLE>", 8ul);
      Appwww(&wsock, wbuf, servercall, 10ul);
      Appwww(&wsock, wbuf, " Status Report</TITLE><link href=\"style.css\" re\
l=\"stylesheet\" type=\"text/css\"></HEAD>\015\012<BODY><div style=\"text-ali\
gn:center\"><H3>\015\012", 131ul);
      if (servercall[0U]) {
         Appwww(&wsock, wbuf, "Server ", 8ul);
         Appwww(&wsock, wbuf, servercall, 10ul);
      }
      if (tcpbindport[0U]) {
         Appwww(&wsock, wbuf, "  Port ", 8ul);
         Appwww(&wsock, wbuf, tcpbindport, 6ul);
      }
      Appwww(&wsock, wbuf, " [udpgate(c) 0.63] Maxusers ", 29ul);
      aprsstr_IntToStr((int32_t)maxusers, 1UL, h1, 256ul);
      Appwww(&wsock, wbuf, h1, 256ul);
      Appwww(&wsock, wbuf, " http#", 7ul);
      aprsstr_IntToStr((int32_t)httpcount, 1UL, h1, 256ul);
      Appwww(&wsock, wbuf, h1, 256ul);
      /*
          IF QWatch.qsize>0 THEN
            Appwww(" txq ");
            IntToStr(QWatch.qsize, 1, h1); Appwww(h1); Appwww("s");
          END;
      */
      Appwww(&wsock, wbuf, " Uptime ", 9ul);
      AppTime(wbuf, &wsock, uptime, 0);
      Appwww(&wsock, wbuf, "</H3></div>\015\012", 14ul);
      klicks(wbuf, &wsock);
      /* udp ports*/
      Appwww(&wsock, wbuf, "<TABLE id=connections BORDER=\"0\" CELLPADDING=3 \
CELLSPACING=1 BGCOLOR=#000000 align=center SUMMARY=\"Connection Table\">\015\01\
2<TR class=tab-con-titel VALIGN=\"BASELINE\" BGCOLOR=\"#A999CC\"><TH COLSPAN=\
14 style=\"text-align:center\">Server Connections</TH></TR>\015\012<TR class=\
tab-con-head BGCOLOR=\"#CCCCFF\">\015\012<TH>Dir</TH><TH>IPnum</TH><TH>Port</\
TH><TH>Call/Port</TH><TH>V</TH> <TH>Software</TH> <TH NOWRAP>Range Filter</TH\
><TH>TxByte</TH> <TH>TxFr</TH><TH NOWRAP>bit/s</TH><TH>RxByte</TH> <TH>RxFr</\
TH><TH NOWRAP>bit/s</TH><TH>Up</TH></TR>", 524ul);
      us = udpsocks;
      while (us) {
         if (us->txbytes>0UL) {
            showpip(us->ip, 0UL, h1, 256ul);
            Appwww(&wsock, wbuf, "<TR class=tab-con-udp BGCOLOR=#E0E0F0 style\
=\"text-align:center\">\015\012<TD>udp</TD><TD>", 83ul);
            Appwww(&wsock, wbuf, h1, 256ul);
            Appwww(&wsock, wbuf, "</TD>", 6ul);
            wint(wbuf, &wsock, (int32_t)us->dport);
            Appwww(&wsock, wbuf, "<TD style=\"text-align:left\">T:", 31ul);
            Appwww(&wsock, wbuf, us->portname, 10ul);
            Appwww(&wsock, wbuf, "</TD><TD></TD><TD></TD>", 24ul);
            if (us->torfradius>=1.0f || us->maxbytes>0UL) {
               Appwww(&wsock, wbuf, "<td>", 5ul);
               if (us->torfradius>=1.0f) {
                  aprsstr_IntToStr((int32_t)(uint32_t)
                X2C_TRUNCC(us->torfradius,0UL,X2C_max_longcard), 1UL, h1,
                256ul);
                  Appwww(&wsock, wbuf, h1, 256ul);
                  Appwww(&wsock, wbuf, "km ", 4ul);
               }
               if (us->maxbytes>0UL) {
                  aprsstr_IntToStr((int32_t)us->maxbytes, 1UL, h1, 256ul);
                  Appwww(&wsock, wbuf, h1, 256ul);
                  Appwww(&wsock, wbuf, "B/s", 4ul);
               }
               Appwww(&wsock, wbuf, "</td>", 6ul);
            }
            else Appwww(&wsock, wbuf, "<TD></TD>", 10ul);
            wint(wbuf, &wsock, (int32_t)us->txbytes);
            wint(wbuf, &wsock, (int32_t)us->txframes);
            if (uptime<systime) ut = systime-uptime;
            else ut = 1UL;
            wint(wbuf, &wsock, (int32_t)((us->txbytes*8UL)/ut));
            wint(wbuf, &wsock, 0L);
            wint(wbuf, &wsock, 0L);
            wint(wbuf, &wsock, 0L);
            Appwww(&wsock, wbuf, "<TD>", 5ul);
            aprsstr_TimeToStr(ut, h1, 256ul);
            Appwww(&wsock, wbuf, h1, 256ul);
            Appwww(&wsock, wbuf, "</TD></TR>", 11ul);
         }
         for (i0 = 0UL; i0<=15UL; i0++) {
            { /* with */
               struct _0 * anonym1 = &us->stat[i0];
               if (anonym1->utime+600UL>systime) {
                  showpip(anonym1->uip, 0UL, h1, 256ul);
                  Appwww(&wsock, wbuf, "<TR class=tab-con-udp BGCOLOR=#E0E0F0\
 style=\"text-align:center\">\015\012<TD>udp</TD><TD>", 83ul);
                  Appwww(&wsock, wbuf, h1, 256ul);
                  Appwww(&wsock, wbuf, "</TD>", 6ul);
                  wint(wbuf, &wsock, (int32_t)anonym1->uport);
                  Appwww(&wsock, wbuf, "<TD style=\"text-align:left\">R:",
                31ul);
                  Appwww(&wsock, wbuf, us->portname, 10ul);
                  Appwww(&wsock, wbuf, "</TD><TD></TD><TD></TD><TD></TD>",
                33ul);
                  wint(wbuf, &wsock, 0L);
                  wint(wbuf, &wsock, 0L);
                  if (anonym1->dtime<systime) ut = systime-anonym1->dtime;
                  else ut = 1UL;
                  wint(wbuf, &wsock, 0L);
                  wint(wbuf, &wsock, (int32_t)anonym1->rxbytes);
                  wint(wbuf, &wsock, (int32_t)anonym1->rxframes);
                  wint(wbuf, &wsock, (int32_t)((anonym1->rxbytes*8UL)/ut));
                  Appwww(&wsock, wbuf, "<TD>", 5ul);
                  aprsstr_TimeToStr(ut, h1, 256ul);
                  Appwww(&wsock, wbuf, h1, 256ul);
                  Appwww(&wsock, wbuf, "</TD></TR>", 11ul);
               }
            }
         } /* end for */
         us = us->next;
      }
      /* tcp ports */
      getlinkfile(serverlink, 1024ul, "serverlink.txt", 15ul);
      ss = tcpsocks;
      while (ss) {
         { /* with */
            struct TCPSOCK * anonym2 = ss;
            if (anonym2->service=='S' || anonym2->service=='G') {
               if (anonym2->connt>0UL && anonym2->connt<systime) {
                  tt = systime-anonym2->connt;
               }
               else tt = 1UL;
               /*
                         Appwww('<TR class=tab-con-tcp style="
                text-align:center" ');
               */
               if (anonym2->service=='S') {
                  Appwww(&wsock, wbuf, "<TR class=tab-con-in style=\"text-ali\
gn:center\" BGCOLOR=\"#C0E0C0\"><TD>in", 72ul);
               }
               else {
                  Appwww(&wsock, wbuf, "<TR class=tab-con-out style=\"text-al\
ign:center\" BGCOLOR=\"#D0C0C0\"><TD>out", 74ul);
               }
               Appwww(&wsock, wbuf, "</TD>", 6ul);
               if (serverlink[0U]==0) {
                  /* make klickable link */
                  Appwww(&wsock, wbuf, "<TD style=\"text-align:center\">",
                31ul);
                  Appwww(&wsock, wbuf, anonym2->ipnum, 64ul);
                  Appwww(&wsock, wbuf, "</TD>", 6ul);
               }
               else {
                  AppCall(wbuf, &wsock, anonym2->ipnum, 64ul, 0, 1,
                serverlink, 1024ul);
               }
               Appwww(&wsock, wbuf, "<TD style=\"text-align:right\">", 30ul);
               if (anonym2->service=='S') {
                  Appwww(&wsock, wbuf, tcpbindport, 6ul);
               }
               else Appwww(&wsock, wbuf, anonym2->port, 6ul);
               Appwww(&wsock, wbuf, "</TD><TD style=\"text-align:left\">",
                34ul);
               Appwww(&wsock, wbuf, anonym2->user.call, 10ul);
               if (anonym2->service=='G' && anonym2->qwatch.qsize>0L) {
                  Appwww(&wsock, wbuf, " q=", 4ul);
                  aprsstr_IntToStr(anonym2->qwatch.qsize, 1UL, h1, 256ul);
                  Appwww(&wsock, wbuf, h1, 256ul);
                  Appwww(&wsock, wbuf, "s", 2ul);
               }
               Appwww(&wsock, wbuf, "</TD><TD>", 10ul);
               if (anonym2->connt>0UL) {
                  if (anonym2->valid) Appwww(&wsock, wbuf, "v", 2ul);
                  if (aprspos_posvalid(anonym2->user.pos)) {
                     Appwww(&wsock, wbuf, "p", 2ul);
                  }
               }
               Appwww(&wsock, wbuf, "</TD><TD WIDTH=50>", 19ul);
               Appwww(&wsock, wbuf, anonym2->vers, 21ul);
               Appwww(&wsock, wbuf, "</TD><TD WIDTH=50>", 19ul);
               if (anonym2->service=='S') {
                  FiltToStr(anonym2->filters, h1, 256ul);
               }
               else aprsstr_Assign(h1, 256ul, anonym2->outfilterst, 256ul);
               Appwww(&wsock, wbuf, h1, 256ul);
               Appwww(&wsock, wbuf, "</TD>", 6ul);
               wcard64(wbuf, &wsock, anonym2->txbytesh, anonym2->txbytes);
               if (anonym2->losttxframes>0UL) {
                  wintint(wbuf, &wsock, (int32_t)anonym2->txframes,
                -(int32_t)anonym2->losttxframes);
               }
               else wint(wbuf, &wsock, (int32_t)anonym2->txframes);
               wint(wbuf, &wsock, (int32_t)bitsec64(anonym2->txbytesh,
                anonym2->txbytes, tt));
               wcard64(wbuf, &wsock, anonym2->rxbytesh, anonym2->rxbytes);
               if (anonym2->lostrxframes>0UL) {
                  wintint(wbuf, &wsock, (int32_t)anonym2->rxframes,
                -(int32_t)anonym2->lostrxframes);
               }
               else wint(wbuf, &wsock, (int32_t)anonym2->rxframes);
               wint(wbuf, &wsock, (int32_t)bitsec64(anonym2->rxbytesh,
                anonym2->rxbytes, tt));
               Appwww(&wsock, wbuf, "<TD>", 5ul);
               if (anonym2->connt>0UL) {
                  aprsstr_TimeToStr(tt, h1, 256ul);
                  Appwww(&wsock, wbuf, h1, 256ul);
               }
               Appwww(&wsock, wbuf, "</TD></TR>", 11ul);
            }
            ss = anonym2->next;
         }
      }
      Appwww(&wsock, wbuf, "</TABLE></BODY></HTML>\015\012", 25ul);
   }
   else if (aprsstr_StrCmp(wsock->get, 256ul, "mh", 3ul)) {
      conthead(&wsock, wbuf);
      title(wbuf, &wsock, &mhhttpcount);
      klicks(wbuf, &wsock);
      if (heardtimew>0UL) {
         showmh(wbuf, &wsock, h1, hearddir, 1, 0, heardtimew, "Heard Stations\
 Since Last ", 27ul, wsock->sortby, 2ul);
      }
      if (heardtimevia>0UL) {
         showmh(wbuf, &wsock, h1, heardvia, 0, 0, heardtimevia, "Via RF Heard\
 Stations Since Last ", 34ul, wsock->sortby, 2ul);
      }
      if (heardtimetcp>0UL) {
         showmh(wbuf, &wsock, h1, heardtcp, 0, 1, heardtimetcp, "Via TCP Conn\
ected Stations Since Last ", 39ul, wsock->sortby, 2ul);
      }
   }
   else if (aprsstr_StrCmp(wsock->get, 256ul, "msg", 4ul)) {
      conthead(&wsock, wbuf);
      title(wbuf, &wsock, &msghttpcount);
      klicks(wbuf, &wsock);
      for (tp = udpgate4_DIR; tp>=udpgate4_NET; tp--) {
         Appwww(&wsock, wbuf, "<table id=msg border=0 align=center CELLPADDIN\
G=3 CELLSPACING=1 BGCOLOR=\"#FFFFFF\">", 83ul);
         if (viacall[0U]==0) {
            if (tp==udpgate4_DIR) {
               strncpy(h1,"Heard Messages (tx off)",256u);
            }
            else if (tp==udpgate4_INDIR) {
               strncpy(h1,"Via Digi Heard Messages (tx off)",256u);
            }
            else strncpy(h1,"Via Net Heard Messages (tx off)",256u);
         }
         else if (tp==udpgate4_DIR) {
            strncpy(h1,"Heard Messages (Relayed to direct heard and Net)",
                256u);
         }
         else if (tp==udpgate4_INDIR) {
            strncpy(h1,"Via Digi Heard Messages (Relayed to direct heard)",
                256u);
         }
         else {
            strncpy(h1,"Via Net Heard Messages (Relayed to direct heard)",
                256u);
         }
         Appwww(&wsock, wbuf, "<tr class=tab-msg-titel BGCOLOR=\"#A9EEFF\"><t\
h colspan=13>", 58ul);
         Appwww(&wsock, wbuf, h1, 256ul);
         Appwww(&wsock, wbuf, "</th></tr>", 11ul);
         hok = 0;
         mp = messages;
         while (mp) {
            if (mp->src==tp) {
               if (!hok) {
                  Appwww(&wsock, wbuf, "<tr class=tab-msg-head BGCOLOR=\"#F0A\
070\"><th>From</th><th>To</th><th>hms ago</th><th>hms tx</th><th>po</th><th>r\
etr</th><th>m>r</th><th>m>n</th><th>a>r</th><th>a>n</th><th>A</th><th>Ack</th\
><th>Text</th></tr>", 209ul);
                  hok = 1;
               }
               Appwww(&wsock, wbuf, "<tr class=tab-msg-body BGCOLOR=\"#DDCC88\\
">", 42ul);
               AppCall(wbuf, &wsock, mp->from, 10ul, 0, 0, "", 1ul);
               AppCall(wbuf, &wsock, mp->to, 10ul, 0, 0, "", 1ul);
               AppTime(wbuf, &wsock, mp->gentime, 1);
               if (mp->retryc>0UL && mp->txtime>0UL) {
                  AppTime(wbuf, &wsock, mp->txtime, 1);
               }
               else if (!mp->acked && mp->gentime+msgsendtime<=systime) {
                  Appwww(&wsock, wbuf, "<td>timed out</td>", 19ul);
               }
               else Appwww(&wsock, wbuf, "<td></td>", 10ul);
               AppInt(wbuf, &wsock, (int32_t)mp->txport);
               AppInt(wbuf, &wsock, (int32_t)mp->retryc);
               AppInt(wbuf, &wsock, (int32_t)mp->msg2rf);
               AppInt(wbuf, &wsock, (int32_t)mp->msg2net);
               AppInt(wbuf, &wsock, (int32_t)mp->ack2rf);
               AppInt(wbuf, &wsock, (int32_t)mp->ack2net);
               Appwww(&wsock, wbuf, "<td>", 5ul);
               if (mp->acked) {
                  if (mp->acksrc==udpgate4_DIR) strncpy(h1,"D",256u);
                  else if (mp->acksrc==udpgate4_INDIR) strncpy(h1,"V",256u);
                  else if (mp->acksrc==udpgate4_NET) strncpy(h1,"N",256u);
                  else strncpy(h1,"X",256u);
                  if (mp->reject) aprsstr_Append(h1, 256ul, "R", 2ul);
                  if (!mp->ackackt) {
                     h1[0U] = (char)((uint32_t)(uint8_t)h1[0U]+32UL);
                  }
                  Appwww(&wsock, wbuf, h1, 256ul);
               }
               Appwww(&wsock, wbuf, "</td>", 6ul);
               aprsstr_Assign(h1, 256ul, mp->ack, 5ul);
               aprsstr_Append(h1, 256ul, mp->replyack, 2ul);
               AppTxt(wbuf, &wsock, "<td>", 5ul, h1, 256ul);
               AppTxt(wbuf, &wsock, "<td>", 5ul, mp->text, 68ul);
               Appwww(&wsock, wbuf, "</tr>\015\012", 8ul);
            }
            mp = mp->next;
         }
         if (hok) {
            Appwww(&wsock, wbuf, "</table>&nbsp;", 15ul);
         }
      } /* end for */
      Appwww(&wsock, wbuf, "</BODY></HTML>\015\012", 17ul);
   }
   else if (wwwdir[0U] && openfile(wsock->get, 256ul, &fdw, &flen)) {
      strncpy(wbuf,"HTTP/1.0 200 OK\015\012Content-Length: ",1401u);
      /*
          +'Content-Type: text/html;
                charset=iso-8859-1'+CR+LF+'Content-Length: ';
      */
      aprsstr_IntToStr(flen, 1UL, h1, 256ul);
      aprsstr_Append(wbuf, 1401ul, h1, 256ul);
      aprsstr_Append(wbuf, 1401ul, "\015\012\015\012", 5ul);
      sendwww(&wsock, wbuf, (int32_t)aprsstr_Length(wbuf, 1401ul), 1);
      while (flen>0L) {
         res0 = osi_RdBin(fdw, (char *)wbuf, 1401u/1u, 1401UL);
         if (res0>0L) {
            sendwww(&wsock, wbuf, res0, 0);
            flen -= res0;
         }
         else flen = 0L;
      }
      osic_Close(fdw);
      wbuf[0U] = 0;
   }
   else strncpy(wbuf,"HTTP/1.1 404\015\012",1401u);
   if (wbuf[0U]) {
      sendwww(&wsock, wbuf, (int32_t)aprsstr_Length(wbuf, 1401ul), 0);
   }
} /* end Www() */


static void Wwwtx(pTCPSOCK * w)
{
   int32_t i0;
   int32_t len;
   struct TCPSOCK * anonym;
   int32_t tmp;
   { /* with */
      struct TCPSOCK * anonym = *w;
      if (anonym->txbuf) {
         if (anonym->txbuf->push) {
            len = sendsock(anonym->fd, anonym->txbuf->buf,
                anonym->txbuf->tlen);
         }
         else {
            len = sendmore(anonym->fd, anonym->txbuf->buf,
                anonym->txbuf->tlen);
         }
         if (len>0L) {
            tmp = anonym->txbuf->tlen-1L;
            i0 = len;
            if (i0<=tmp) for (;; i0++) {
               anonym->txbuf->buf[i0-len] = anonym->txbuf->buf[i0];
                /* delete the sent */
               if (i0==tmp) break;
            } /* end for */
            anonym->txbuf->tlen -= len;
            if (anonym->connt==0UL) anonym->connt = systime;
         }
         else anonym->txbuf->tlen = 0L;
         if (anonym->txbuf->tlen<=0L) closetcp(w, 0);
      }
   }
} /* end Wwwtx() */


static void saybusy(int32_t * fd, char s[], uint32_t s_len)
{
   int32_t res0;
   X2C_PCOPY((void **)&s,s_len);
   if (*fd>=0L) {
      res0 = sendsock(*fd, s, (int32_t)aprsstr_Length(s, s_len));
      osic_Close(*fd);
      *fd = -1L;
   }
   X2C_PFREE(s);
} /* end saybusy() */


static void KillDoubleUser(pTCPSOCK u)
{
   pTCPSOCK v;
   v = u->next;
   while (v) {
      if ((v->service=='S' && v->user.call[0U])
                && aprsstr_StrCmp(u->user.call, 10ul, v->user.call, 10ul)) {
         if (v->valid && !u->valid) {
            saybusy(&u->fd, "# unvalidated double login\015\012", 29ul);
         }
         else {
            saybusy(&v->fd, "# double login\015\012", 17ul);
                /* kill older or the unvalid */
         }
      }
      v = v->next;
   }
} /* end KillDoubleUser() */


static char tcpconn(pTCPSOCK * sockchain, int32_t f,
                char cservice)
{
   pTCPSOCK cp;
   FRAMEBUF h2;
   FRAMEBUF h1;
   FRAMEBUF h;
   int32_t res0;
   struct TCPSOCK * anonym;
   if (f<0L) return 0;
   osic_alloc((char * *) &cp, sizeof(struct TCPSOCK));
   if (cp==0) {
      osic_Close(f);
      if (verb) osi_WrStrLn("tcp conn out of memory", 23ul);
      return 0;
   }
   memset((char *)cp,(char)0,sizeof(struct TCPSOCK));
   { /* with */
      struct TCPSOCK * anonym = cp;
      anonym->fd = f;
      anonym->valid = cservice=='G';
      anonym->service = cservice;
      res0 = socknonblock(f);
      anonym->txbuf = 0;
   }
   cp->next = *sockchain;
   *sockchain = cp;
   if (cservice=='G') {
      if (servercall[0U]) {
         aprsstr_Assign(h, 512ul, "user ", 6ul);
         aprsstr_Append(h, 512ul, servercall, 10ul);
         if (passwd[0U]) {
            aprsstr_Append(h, 512ul, " pass ", 7ul);
            aprsstr_Append(h, 512ul, passwd, 6ul);
         }
         aprsstr_Append(h, 512ul, " vers ", 7ul);
         aprsstr_Append(h, 512ul, "udpgate(c) 0.63", 16ul);
         if (actfilter[0U]) {
            aprsstr_Append(h, 512ul, " filter ", 9ul);
            aprsstr_Append(h, 512ul, actfilter, 256ul);
         }
         aprsstr_Append(h, 512ul, "\015\012", 3ul);
         Sendtcp(cp, h);
      }
   }
   else if (cservice=='S') {
      /*
      cp^.txbytes:=4294967200;
      cp^.txbytesh:=0;
      cp^.rxbytes:=4294965296;
      cp^.rxbytesh:=0;
      */
      res0 = 512L;
      res0 = getpeeripnum(f, h1, &res0);
      if (res0>=0L) {
         /*
                   tcp.ipnum2str(h1, h, SIZE(h));
         */
         ipnumport2str(h1, 512UL, h, 512UL, h2, 512UL);
         aprsstr_Assign(cp->ipnum, 64ul, h, 512ul);
         aprsstr_Assign(cp->port, 6ul, h2, 512ul);
         strncpy(h1,"T:connect to: ",512u);
         aprsstr_Append(h1, 512ul, h, 512ul);
         aprsstr_Append(h1, 512ul, " ", 2ul);
         aprsstr_Append(h1, 512ul, h2, 512ul);
         logline(1L, h1, 512ul);
      }
      aprsstr_Assign(h, 512ul, "# udpgate(c) 0.63\015\012", 20ul);
      Sendtcp(cp, h);
   }
   return 1;
} /* end tcpconn() */


static void Gateconn(pTCPSOCK * cp)
{
   int32_t fd;
   pTCPSOCK kill;
   pTCPSOCK try0;
   pTCPSOCK act;
   pTCPSOCK p;
   FRAMEBUF h;
   char hh[21];
   uint32_t max0;
   struct _1 * anonym;
   act = 0;
   try0 = 0;
   p = *cp;
   while (p) {
      if (p->service=='G') {
         if (p->connt>0UL) {
            /* we have gate connect */
            if (act) {
               /* we have 2 connects */
               if (act->gatepri>p->gatepri) {
                  /* stop second best */
                  kill = act;
                  act = p;
               }
               else kill = p;
               saybusy(&kill->fd, "# remove double connect\015\012", 26ul);
            }
            else act = p;
         }
         else try0 = p;
      }
      p = p->next;
   }
   if (try0==0) {
      if (gatesfn[0U]) readurlsfile(gatesfn, 1024ul);
      if (act==0) max0 = 20UL;
      else max0 = act->gatepri;
      if ((trygate>=max0 || trygate>20UL) || gateways[trygate].url[0U]==0) {
         trygate = 0UL; /* loop thru table */
      }
      if (lastdnstime>systime) lastdnstime = systime;
      if ((trygate<max0 && Watchclock(&gateways[trygate].connecttime,
                gateconndelay)) && lastdnstime+gateways[trygate].resolvtime*5UL<=systime)
                 {
         { /* with */
            struct _1 * anonym = &gateways[trygate];
            if (anonym->url[0U]) {
               /*check dns time  */
               spintime();
               lastdnstime = systime;
               /*WrStrLn("start resolve"); */
               fd = connectto(anonym->url, anonym->port);
               spintime();
               anonym->resolvtime = systime-lastdnstime;
               if (anonym->resolvtime>5UL) {
                  strncpy(h,"T:slow DNS ",512u);
                  aprsstr_IntToStr((int32_t)anonym->resolvtime, 1UL, hh,
                21ul);
                  aprsstr_Append(h, 512ul, hh, 21ul);
                  aprsstr_Append(h, 512ul, "s", 2ul);
                  logline(1L, h, 512ul);
                  if (anonym->resolvtime>300UL) anonym->resolvtime = 120UL;
               }
               /*check dns time */
               if (fd>=0L) {
                  if (anonym->filterst[0U]) {
                     aprsstr_Assign(actfilter, 256ul, anonym->filterst,
                256ul);
                  }
                  else {
                     aprsstr_Assign(actfilter, 256ul, serverrangefilter,
                256ul);
                  }
                  if (tcpconn(cp, fd, 'G')) {
                     aprsstr_Assign((*cp)->ipnum, 64ul, anonym->url, 256ul);
                     aprsstr_Assign((*cp)->port, 6ul, anonym->port, 6ul);
                     aprsstr_Assign((*cp)->outfilterst, 256ul,
                anonym->filterst, 256ul);
                     (*cp)->gatepri = trygate;
                     strncpy(h,"T:connect to: ",512u);
                     aprsstr_Append(h, 512ul, anonym->url, 256ul);
                     aprsstr_Append(h, 512ul, " ", 2ul);
                     aprsstr_Append(h, 512ul, anonym->port, 6ul);
                     logline(1L, h, 512ul);
                  }
               }
            }
         }
      }
      ++trygate;
   }
} /* end Gateconn() */


static void WatchTXQ(pTCPSOCK sock)
{
   int32_t sent;
   int32_t j;
   int32_t i0;
   int32_t rb;
   int32_t acked;
   int32_t qb;
   char rest;
   int32_t tmp;
   qb = getunack(sock->fd); /* tcp unack sendqueue in byte */
   sent = (int32_t)(sock->txbytes-sock->qwatch.lasttb);
                /* last second sent bytes */
   if (qb<=0L || sent<0L) sock->qwatch.qsize = 0L;
   else {
      /* we have unack bytes */
      acked = (sock->qwatch.lastqb-qb)+sent; /* acked bytes in last s */
      j = 0L;
      rest = 0;
      tmp = sock->qwatch.qsize-1L;
      i0 = 0L;
      if (i0<=tmp) for (;; i0++) {
         rb = sock->qwatch.txbyte[i0]-acked;
         sock->qwatch.txbyte[j] = rb;
         if (rb>0L) rest = 1;
         else acked = -rb;
         if (rest) {
            acked = 0L;
            ++j;
         }
         if (i0==tmp) break;
      } /* end for */
      sock->qwatch.qsize = j;
      if (sock->qwatch.qsize<=59L) {
         sock->qwatch.txbyte[sock->qwatch.qsize] = sent;
         ++sock->qwatch.qsize;
         if (sock->qwatch.qsize>qmaxtime) {
            stoptxrx(sock->fd, 2L); /* delete unsent data */
            saybusy(&sock->fd, "\015\012", 3ul); /* close tcp immediately */
            sock->slowlink = 1; /* for log message */
         }
      }
   }
   sock->qwatch.lastqb = qb;
   sock->qwatch.lasttb = sock->txbytes;
} /* end WatchTXQ() */

static int32_t listensock;

static int32_t wwwsock;

static FRAMEBUF mbuf;

static int32_t res;

static int32_t i;

static pTCPSOCK acttcp;

static pTCPSOCK acttmp;

static pUDPSOCK actudp;

static uint32_t usercnt;

static uint32_t lenh;

static struct POSCALL poscall;

static char voidungate;

static char rfhered;

static uint32_t keepconn;

static uint32_t lasttime;


static void addsock(int32_t fd, char wtoo)
/* insert socket in fdset for select */
{
   if (fd>=0L) {
      fdsetr((uint32_t)fd);
      if (wtoo) fdsetw((uint32_t)fd);
   }
} /* end addsock() */

static CHSET _cnst1 = {0x30000000UL,0x20008092UL,0x80000001UL,0x00000001UL};

X2C_STACK_LIMIT(100000l)
extern int main(int argc, char **argv)
{
   X2C_BEGIN(&argc,argv,1,4000000l,8000000l);
   if (sizeof(MONCALL)!=10) X2C_ASSERT(0);
   if (sizeof(FILENAME)!=1024) X2C_ASSERT(0);
   if (sizeof(FRAMEBUF)!=512) X2C_ASSERT(0);
   if (sizeof(FILTERST)!=256) X2C_ASSERT(0);
   if (sizeof(WWWB)!=1401) X2C_ASSERT(0);
   if (sizeof(MSGTEXT)!=68) X2C_ASSERT(0);
   if (sizeof(ACKTEXT)!=5) X2C_ASSERT(0);
   if (sizeof(REPLYACK)!=2) X2C_ASSERT(0);
   aprsstr_BEGIN();
   aprspos_BEGIN();
   osi_BEGIN();
   strncpy(rfdestcall,"RFONLY",10u);
   strncpy(netdestcall,"NOGATE",10u);
   viacall[0U] = 0;
   nettorfpath[0U] = 0;
   heardtime = 7200UL;
   heardtimew = 86400UL;
   heardtimevia = 1800UL;
   heardtimetcp = 86400UL;
   purgemsg = 86400UL;
   purgeacked = 86400UL;
   purgeunack = 3600UL;
   purgeunacksent = 30UL;
   gateconndelay = 30UL;
   msgsendtime = 21600UL;
   rfquiet = 10UL;
   maxatonce = 2UL;
   maxmsg = 1000UL;
   messages = 0;
   hearddir = 0;
   heardvia = 0;
   heardtcp = 0;
   lastrfsent = 0UL;
   sendnetmsg = 1;
   mhfilelines = 0UL;
   memset((char *)gateways,(char)0,sizeof(struct _1 [21]));
   realtime = 0UL;
   systime = 946728000UL; /* start of systime about 1.1.2000 */
   qmaxtime = 15L;
   ungates[udpgate4_gUNGATE][0] = 0;
   strncpy(ungates[udpgate4_gRFONLY],"RFONLY",11u);
   strncpy(ungates[udpgate4_gNOGATE],"NOGATE",11u);
   strncpy(ungates[udpgate4_gTCPIP],"TCPIP",11u);
   strncpy(ungates[udpgate4_gTCPXX],"TCPXX",11u);
   strncpy(ungates[udpgate4_gQ],"q",11u);
   dupetime = 60UL;
   lastdnstime = 0UL;
   netbeaconfn[0U] = 0;
   maxusers = 50UL;
   logframename[0U] = 0;
   rawlogname[0U] = 0;
   logframes = 6L;
   tcpbindport[0U] = 0;
   servercall[0U] = 0;
   passwd[0U] = 0;
   serverrangefilter[0U] = 0;
   actfilter[0U] = 0;
   rfhered = 0;
   wwwbindport[0] = 0;
   wwwdir[0U] = 0;
   wwwsizelimit = 1048576L;
   udpsocks = 0;
   callsrc = 0;
   mhperport = 0;
   qas = 0UL;
   qasc = 0UL;
   maxpongtime = 30UL;
   gatesfn[0U] = 0;
   parms();
   if (aprsstr_StrCmp(viacall, 10ul, "-", 2ul)) viacall[0U] = 0;
   else if (viacall[0U]==0) memcpy(viacall,servercall,10u);
   msghttpcount = 0UL;
   mhhttpcount = 0UL;
   netmhout = 0UL;
   netmhin = 0UL;
   aprsstr_Assign(ungates[udpgate4_gUNGATE], 11ul, servercall, 10ul);
   uptime = systime;
   udpdonetime = systime;
   keepconn = 0UL;
   trygate = 0UL;
   httpcount = 0UL;
   Netbeacon(mbuf, 512ul, 0, 1);
   listensock = -1L;
   wwwsock = -1L;
   tcpsocks = 0;
   for (;;) {
      spintime();
      if (systime!=lasttime) {
         if (listensock<0L && tcpbindport[0U]) {
            /* open listensocket tcp connects */
            listensock = waitconnect(tcpbindport, 4UL);
            if (verb && listensock<0L) {
               osi_WrStr("cant bind to port ", 19ul);
               osi_WrStrLn(tcpbindport, 6ul);
            }
         }
         if (wwwsock<0L && wwwbindport[0U]) {
            /* open listensocket www connects */
            wwwsock = waitconnect(wwwbindport, 16UL);
            if (verb && wwwsock<0L) {
               osi_WrStr("cant bind to port ", 19ul);
               osi_WrStrLn(wwwbindport, 6ul);
            }
         }
      }
      fdclr();
      actudp = udpsocks;
      while (actudp) {
         addsock(actudp->fd, 0);
         actudp = actudp->next;
      }
      addsock(listensock, 0);
      addsock(wwwsock, 0);
      if (systime!=lasttime) {
         /* once a second is enough */
         RfTimer();
         SendNet();
         if (mhfilelines>0UL) MHtoFile();
         rfhered = keeptime==0UL || keepconn>systime;
         acttcp = tcpsocks;
         while (acttcp) {
            { /* with */
               struct TCPSOCK * anonym = acttcp;
               if (anonym->beacont<systime) Timebeacon(acttcp);
               if (anonym->service=='G') {
                  if (keeptime>0UL && keepconn<=systime) {
                     saybusy(&anonym->fd, "# timeout\015\012", 12ul);
                  }
                  else if (qmaxtime>0L) WatchTXQ(acttcp);
               }
               else if (anonym->service=='W') {
                  if (anonym->connt==0UL) anonym->connt = systime;
                  else if (anonym->connt+60UL<systime) {
                     saybusy(&anonym->fd, "timeout\015\012", 10ul);
                  }
               }
               else if (anonym->service=='S') {
                  KillDoubleUser(acttcp);
                  keepconn = systime+keeptime; /* connect to gateway */
               }
               acttcp = anonym->next;
            }
         }
      }
      if (systime!=lasttime) {
         if (rfhered) Gateconn(&tcpsocks);
         else trygate = 0UL;
      }
      lasttime = systime;
      usercnt = 0UL;
      acttcp = tcpsocks;
      while (acttcp) {
         addsock(acttcp->fd, acttcp->tlen>0L || acttcp->txbuf);
         ++usercnt;
         acttcp = acttcp->next;
      }
      mbuf[0U] = 0;
      res = selectrw(2UL, 0UL);
      if (listensock>=0L && issetr((uint32_t)listensock)) {
         res = 512L;
         res = acceptconnect(listensock, mbuf, &res);
         if (res>=0L) {
            if (usercnt>=maxusers || !tcpconn(&tcpsocks, res, 'S')) {
               saybusy(&res, "# server full\015\012", 16ul);
            }
         }
      }
      if (wwwsock>=0L && issetr((uint32_t)wwwsock)) {
         res = 512L;
         res = acceptconnect(wwwsock, mbuf, &res);
         if (res>=0L) {
            if (usercnt>=maxusers+1UL || !tcpconn(&tcpsocks, res, 'W')) {
               saybusy(&res, "server full\015\012", 14ul);
            }
         }
      }
      spintime();
      actudp = udpsocks;
      i = 1L;
      while (actudp) {
         if (issetr((uint32_t)actudp->fd)) {
            while (getudp(actudp, mbuf, actudp->ghosts)) {
               if (systime<udpdonetime+5UL) {
                  /* last time all data read is no too long */
                  res = AprsIs(mbuf, 512ul, 0x3FU, 0x3FU, "", 1ul,
                (uint32_t)i, 0, &poscall);
                  if (res>=0L) {
                     Sendall(mbuf, 0L, poscall);
                     keepconn = systime+keeptime; /* connect to gateway */
                  }
               }
               else {
                  /*task was blocked too long */
                  if (verb) {
                     osi_WrStrLn("AXUDP-frames thrown away - too long program\
 delay", 50ul);
                  }
                  logline(1L, "Frame discarded - too long program delay",
                41ul);
               }
               if (verb || logframes>1L) {
                  showframe(res, 0, actudp, mbuf, 512ul, poscall.pos);
               }
            }
         }
         ++i;
         actudp = actudp->next;
      }
      spintime();
      udpdonetime = systime; /* store fresh time of last all read input */
      acttcp = tcpsocks;
      while (acttcp) {
         if (acttcp->fd>=0L && issetw((uint32_t)acttcp->fd)) {
            { /* with */
               struct TCPSOCK * anonym0 = acttcp;
               if (anonym0->service=='W') Wwwtx(&acttcp);
               else {
                  sendtcpbuf(acttcp);
                  if (anonym0->connt==0UL) anonym0->connt = systime;
               }
            }
         }
         if (acttcp->fd>=0L && issetr((uint32_t)acttcp->fd)) {
            if (acttcp->service=='W') Www(acttcp);
            else {
               for (;;) {
                  res = Gettcp(acttcp->fd, mbuf, acttcp->rbuf,
                &acttcp->rpos);
                  if (res<0L) {
                     osic_Close(acttcp->fd);
                     acttcp->fd = -1L;
                     break;
                  }
                  if (res<=0L) break;
                  ++acttcp->rxframes;
                  lenh = aprsstr_Length(mbuf, 512ul);
                  acttcp->rxbytes += lenh;
                  if (acttcp->rxbytes<lenh) ++acttcp->rxbytesh;
                  if (acttcp->pongtime>0UL && acttcp->service=='S') {
                     /* pingpong active */
                     if (acttcp->pongtime<systime) {
                        /* pinpong enabled - stop transfer */
                        saypongout(acttcp);
                        acttcp->pingout = 1;
                        ++acttcp->lostrxframes;
                     }
                  }
                  if ((acttcp->valid || acttcp->service=='G') || !Auth(mbuf,
                512ul, acttcp)) {
                     if (mbuf[0U]=='#') {
                        GetFilters(&acttcp->filters, mbuf, 512ul, 1UL,
                acttcp);
                        res = -7L;
                     }
                     else {
                        res = AprsIs(mbuf, 512ul, 0x3U, 0x17U,
                acttcp->user.call, 10ul, 0UL, acttcp->valid, &poscall);
                     }
                     if (((res>=-1L && aprspos_posvalid(poscall.pos))
                && aprsstr_StrCmp(poscall.call, 10ul, acttcp->user.call,
                10ul)) && X2C_INL((int32_t)(uint8_t)poscall.typ0,128,
                _cnst1)) {
                        acttcp->user.pos = poscall.pos;
                        if (heardtimetcp>0UL) {
                           AddHeard(&heardtcp, heardtimetcp, poscall.call,
                0UL, mbuf, 512ul, &voidungate, 0);
                /* store pos in mh to remember pos at next connect */
                        }
                     }
                     if (acttcp->valid && !acttcp->pingout) {
                        if (res>=0L) Sendall(mbuf, acttcp->fd, poscall);
                        if (verb || logframes>1L) {
                           showframe(res, acttcp, 0, mbuf, 512ul,
                poscall.pos);
                        }
                     }
                  }
               }
            }
         }
         if (acttcp->fd<0L) {
            /* connection lost */
            if (acttcp->connt && acttcp->service!='W') showlogout(acttcp);
            closetcp(&acttcp, 1); /* free www buffers */
            if (acttcp==tcpsocks) tcpsocks = tcpsocks->next;
            else {
               acttmp = tcpsocks;
               while (acttmp->next!=acttcp) acttmp = acttmp->next;
               acttmp->next = acttcp->next;
            }
            osic_free((char * *) &acttcp, sizeof(struct TCPSOCK));
            acttcp = tcpsocks;
         }
         else acttcp = acttcp->next;
      }
   }
   X2C_EXIT();
   return 0;
}

X2C_MAIN_DEFINITION
