/*
 * 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 l2cat_C_
#ifndef Execlogin_H_
#include "Execlogin.h"
#endif
#include <unistd.h>
#include <signal.h>
#ifndef Select_H_
#include "Select.h"
#endif
#ifndef cleanup_H_
#include "cleanup.h"
#endif
#ifndef aprsstr_H_
#include "aprsstr.h"
#endif
#ifndef osi_H_
#include "osi.h"
#endif
#include <osic.h>
#ifndef mlib_H_
#include "mlib.h"
#endif
#ifndef udp_H_
#include "udp.h"
#endif
#ifndef l2_H_
#include "l2.h"
#endif
#ifndef frameio_H_
#include "frameio.h"
#endif
#ifndef deflate_H_
#include "deflate.h"
#endif

/* layer2 to std in/out or start a task*/
/*
FROM mlib IMPORT tcsetattr, tcgetattr, tcflag_t, 
                 TCSAFLUSH, TIOCMGET, TIOCMBIS, TIOCMBIC, TCSANOW, ISIG,
                 CS8, CLOCAL, CREAD, CRTSCTS, B1200, B2400, 
                 B4800, B9600, B19200, B38400, B57600, B115200, B230400, 
                 B460800, TIOCM_CD, TIOCM_DTR, TIOCSCTTY;
*/
#define l2cat_MAXCMDWORDS 64

#define l2cat_NL "\012"

#define l2cat_PPPFLAG 0x7E 

#define l2cat_PPPESC 0x7D 

#define l2cat_LF "\012"

#define l2cat_LOOPTIME 10000

#define l2cat_MINRECONTIME 2000000

#define l2cat_MAXRECONTIME 120000000

#define l2cat_RCTIMEBASE 1000

typedef char IOBUF[256];

struct JUNKBUF;


struct JUNKBUF {
   IOBUF buf;
   int32_t len;
   int32_t time0;
};

struct DEFLATBUF;


struct DEFLATBUF {
   char outbuf[20001]; /* 20000 */
   int32_t outlen;
   char xoutbuf[4096];
   char pppbuf[4096];
   int32_t ppplen;
   int32_t xoutlen;
   int32_t inp0;
   int32_t xinp;
   char pppstate;
   struct deflate_CONTEXT deflatcontext;
   struct deflate_XCONTEXT expandcontext;
   char done;
   char pppframing;
};

struct TASK;

typedef struct TASK * pTASK;


struct TASK {
   pTASK next;
   IOBUF inbuf;
   IOBUF outbuf;
   int32_t inlen;
   int32_t outlen;
   struct JUNKBUF junkbuf;
   struct l2_GETADRESS l2addr;
   l2_pLINK link0;
   uint16_t events;
   int32_t infd;
   int32_t outfd;
   uint8_t state;
   uint32_t crlfmode;
   uint32_t detectdone;
   uint32_t inignor;
   struct DEFLATBUF * pcdeflat;
};

static char terminate;

static char keepconnected;

static char verb;

static char verb2;

static char usedeflat;

static char pppmode;

static char autodet;

static pTASK tasks;

static uint32_t sockc;

static uint32_t convlfcr;

static uint32_t convtopipe;

static uint32_t montyp;

static uint32_t recontime;

static uint32_t minrecontime;

static uint32_t maxrecontime;

static int32_t timesum;

static int32_t pppdiscard;

static char mycall[113];

static char cmdline[4096];

static char pipename[4096];

static char connectto0[256]; /* if active connect to this server */

static struct l2_PARMS0 parms;

struct _0;


struct _0 {
   int32_t port;
   int32_t parmnum;
   int32_t val;
};

static struct _0 numpar[256];

static l2_pLINK pmon;


static void Error(char text[], uint32_t text_len)
{
   X2C_PCOPY((void **)&text,text_len);
   osi_Werr(text, text_len);
   osi_Werr(" error abort\012", 14ul);
   X2C_ABORT();
   X2C_PFREE(text);
} /* end Error() */

#define l2cat_QUOT "\""

#define l2cat_SEP " "


static void NextArgs(char s[], uint32_t s_len)
{
   char h[65536];
   int32_t l;
   s[0UL] = 0;
   l = 0L;
   do {
      osi_NextArg(h, 65536ul);
      if (l==0L) {
         if (h[0U]=='\"') aprsstr_Delstr(h, 65536ul, 0UL, 1UL);
         else {
            aprsstr_Append(s, s_len, h, 65536ul);
            return;
         }
      }
      l = (int32_t)aprsstr_Length(h, 65536ul);
      if (l==0L) return;
      if (h[l-1L]=='\"') {
         aprsstr_Delstr(h, 65536ul, (uint32_t)(l-1L), 1UL);
         l = 0L;
      }
      aprsstr_Append(s, s_len, " ", 2ul);
      aprsstr_Append(s, s_len, h, 65536ul);
   } while (l);
} /* end NextArgs() */


static void Whex(char hd[], uint32_t hd_len, const char bu[],
                uint32_t bu_len, int32_t len)
{
   int32_t i0;
   uint32_t b;
   int32_t tmp;
   char tmp0;
   X2C_PCOPY((void **)&hd,hd_len);
   osi_Werr(hd, hd_len);
   tmp = len-1L;
   i0 = 0L;
   if (i0<=tmp) for (;; i0++) {
      b = (uint32_t)(uint8_t)bu[i0]/16UL;
      if (b>=10UL) {
         osi_Werr((char *)(tmp0 = (char)(b+55UL),&tmp0), 1u/1u);
      }
      else osi_Werr((char *)(tmp0 = (char)(b+48UL),&tmp0), 1u/1u);
      b = (uint32_t)(uint8_t)bu[i0]&15UL;
      if (b>=10UL) {
         osi_Werr((char *)(tmp0 = (char)(b+55UL),&tmp0), 1u/1u);
      }
      else osi_Werr((char *)(tmp0 = (char)(b+48UL),&tmp0), 1u/1u);
      if ((i0&31L)==31L) osi_Werr("\012", 2ul);
      else osi_Werr(" ", 2ul);
      if (i0==tmp) break;
   } /* end for */
   osi_Werr("\012", 2ul);
   X2C_PFREE(hd);
} /* end Whex() */


static void CallStr(char c[], uint32_t c_len, uint32_t n,
                char withssid, char s[], uint32_t s_len)
{
   uint32_t p;
   uint32_t i0;
   X2C_PCOPY((void **)&c,c_len);
   n = n*7UL;
   p = 0UL;
   for (i0 = 0UL; i0<=5UL; i0++) {
      if (c[i0+n]!=' ') {
         s[p] = c[i0+n];
         ++p;
      }
   } /* end for */
   s[p] = 0;
   if (withssid) {
      i0 = (uint32_t)(uint8_t)c[n+6UL]&15UL;
      if (i0) {
         s[p] = '-';
         ++p;
         if (i0>=10UL) {
            s[p] = (char)(i0/10UL+48UL);
            ++p;
         }
         s[p] = (char)(i0%10UL+48UL);
         ++p;
      }
      s[p] = 0;
   }
   X2C_PFREE(c);
} /* end CallStr() */


static void WerrCall(pTASK task)
{
   char h[256];
   if (verb && task) {
      CallStr(task->l2addr.adress, 72ul, 0UL, 1, h, 256ul);
      osi_Werr(h, 256ul);
   }
} /* end WerrCall() */


static void GetCall(const char from[], uint32_t from_len,
                uint32_t * p, l2_pSTRING to)
{
   uint16_t len;
   uint16_t i0;
   len = (uint16_t)aprsstr_Length(from, from_len);
   while (*p<(uint32_t)len && (uint8_t)from[*p]<=' ') ++*p;
   for (i0 = 0U; i0<=5U; i0++) {
      if ((*p<(uint32_t)len && from[*p]!='-') && from[*p]!=' ') {
         to[i0] = X2C_CAP(from[*p]);
         ++*p;
      }
      else to[i0] = ' ';
   } /* end for */
   i0 = 0U;
   if (from[*p]=='-') {
      ++*p;
      if ((uint8_t)from[*p]>='0' && (uint8_t)from[*p]<='9') {
         i0 = (uint16_t)((uint32_t)(uint8_t)from[*p]-48UL);
         ++*p;
      }
      if ((uint8_t)from[*p]>='0' && (uint8_t)from[*p]<='9') {
         i0 = (uint16_t)(((uint32_t)(i0*10U)+(uint32_t)(uint8_t)
                from[*p])-48UL);
         ++*p;
      }
   }
   to[6U] = (char)((i0&15U)+48U);
} /* end GetCall() */

static void Event(char * *, l2_pLINK, uint8_t);


static void Event(char * * atask, l2_pLINK link0, uint8_t event)
{
   struct l2_GETADRESS gadr;
   pTASK ntask;
   struct l2_CONNECT connect;
   pTASK ptask;
   if (event==l2_eCONNREQ) {
      osic_alloc((char * *) &ntask, sizeof(struct TASK));
      if (ntask==0) return;
      memset((char *)ntask,(char)0,sizeof(struct TASK));
      ntask->infd = -1L;
      ntask->outfd = -1L;
      ntask->link0 = link0;
      ntask->state = 1U;
      ntask->crlfmode = convlfcr;
      ntask->detectdone = (uint32_t)autodet;
      l2_GetAdress0(link0, &ntask->l2addr);
      memset((char *) &connect,(char)0,sizeof(struct l2_CONNECT));
      connect.port = ntask->l2addr.port;
      connect.baud = 1U;
      connect.cpid = 240U;
      connect.l2adr = (l2_pSTRING)ntask->l2addr.adress;
      connect.typ = l2_cCONNAK;
      ntask->link0 = l2_Connect0((char *)ntask, &connect);
      ntask->next = tasks;
      tasks = ntask;
      if (verb) {
         WerrCall(ntask);
         osi_Werr(" connreq\012", 10ul);
      }
   }
   else if (event==l2_eFLEXDATA) l2_GetAdress0(link0, &gadr);
   else if (event==l2_eCONNECTED) {
      /*WrStr(gadr.adress); */
      /*WrInt(dbuf^.len, 10); */
      /*WrStrLn(" flexdata"); */
      if (verb) {
         WerrCall((pTASK)*atask);
         osi_Werr(" connected!\012", 13ul);
      }
      if (*atask) {
         ptask = (pTASK)*atask;
         ptask->state = 1U;
      }
   }
   else if (*atask) {
      /*  ELSIF event=eDISCONNECTED THEN */
      /*WrInt(ORD(event), 1);WrStrLn(":host got event"); */
      ptask = (pTASK)*atask;
      ptask->events |= (1U<<event);
   }
} /* end Event() */


static void GetWord(char s[], uint32_t s_len, char w[],
                uint32_t w_len)
{
   uint32_t j;
   uint32_t i0;
   char e;
   i0 = 0UL;
   j = 0UL;
   e = ' ';
   if (s[0UL]=='\"') {
      e = '\"';
      s[0UL] = ' ';
      i0 = 1UL;
   }
   for (;;) {
      if (i0>s_len-1 || s[i0]==0) break;
      if (s[i0]==e) {
         s[i0] = ' ';
         break;
      }
      if (j<w_len-1) {
         w[j] = s[i0];
         ++j;
      }
      s[i0] = ' ';
      ++i0;
   }
   w[j] = 0;
} /* end GetWord() */


static void taskparms(char s[], uint32_t s_len, char argbuf[],
                uint32_t argbuf_len, char * argwords[],
                uint32_t argwords_len)
{
   uint32_t i0;
   uint32_t argc;
   uint32_t argp;
   char h[1024];
   X2C_PCOPY((void **)&s,s_len);
   memset((char *)argwords,(char)0,argwords_len*sizeof(char *));
   argc = 0UL;
   argp = 0UL;
   for (;;) {
      while (s[0UL]==' ') aprsstr_Delstr(s, s_len, 0UL, 1UL);
      GetWord(s, s_len, h, 1024ul);
      /*WrStrLn(h); */
      h[1023U] = 0;
      if (h[0U]==0) break;
      argwords[argc] = (char *) &argbuf[argp];
      i0 = 0UL;
      for (;;) {
         if (argp>=argbuf_len-1) {
            /*argbuffer full*/
            argbuf[argbuf_len-1] = 0;
            osi_Werr("ArgBuf full!\012", 14ul);
            break;
         }
         argbuf[argp] = h[i0];
         ++argp;
         if (h[i0]==0) break;
         ++i0;
      }
      ++argc;
      if (argc>=argwords_len-1) break;
   }
   argwords[argc] = 0;
   X2C_PFREE(s);
} /* end taskparms() */

#define l2cat_M "%"


static void Makros(char c[], uint32_t c_len, char a[],
                uint32_t a_len, char s[], uint32_t s_len)
{
   uint32_t i0;
   char h[16];
   char m;
   X2C_PCOPY((void **)&c,c_len);
   X2C_PCOPY((void **)&a,a_len);
   s[0UL] = 0;
   i0 = 0UL;
   m = 0;
   while (i0<=c_len-1 && c[i0]) {
      if (m) {
         if (c[i0]=='%') aprsstr_Append(s, s_len, "%", 2ul);
         else if (c[i0]=='m') {
            CallStr(a, a_len, 1UL, 1, h, 16ul);
            aprsstr_Append(s, s_len, h, 16ul);
         }
         else if (c[i0]=='u') {
            CallStr(a, a_len, 0UL, 1, h, 16ul);
            aprsstr_Append(s, s_len, h, 16ul);
         }
         else if (c[i0]=='U') {
            CallStr(a, a_len, 0UL, 0, h, 16ul);
            aprsstr_Append(s, s_len, h, 16ul);
         }
         else {
            aprsstr_Append(s, s_len, "%", 2ul);
            aprsstr_Append(s, s_len, (char *) &c[i0], 1u/1u);
         }
         m = 0;
      }
      else if (c[i0]=='%') m = 1;
      else aprsstr_Append(s, s_len, (char *) &c[i0], 1u/1u);
      ++i0;
   }
   X2C_PFREE(c);
   X2C_PFREE(a);
} /* end Makros() */


static void Login(int32_t * fd, char l2adr[], uint32_t l2adr_len)
{
   struct Execlogin_tEXEC cmdvec;
   char argstr[8192];
   char argbuf[8192];
   char * argwords[65];
   X2C_PCOPY((void **)&l2adr,l2adr_len);
   /*args:="/bin/login login"; */
   Makros(cmdline, 4096ul, l2adr, l2adr_len, argstr, 8192ul);
   taskparms(argstr, 8192ul, argbuf, 8192ul, argwords, 65ul);
   /*p:=argwords[0];WrLn;WrInt(CAST(CARDINAL, argwords[0]), 15);
                WrInt(CAST(CARDINAL, p), 15);WrLn; */
   /*WrStrLn(p^);p:=argwords[1];WrStrLn(p^);WrStrLn(argbuf); */
   *fd = -1L;
   cmdvec.cmdfn = (char *)argwords[0U];
   cmdvec.args = (X2C_pCHAR *) &argwords[1U];
   if (verb) {
      osi_Werr("start: ", 8ul);
      osi_Werr(cmdline, 4096ul);
      osi_Werr("\012", 2ul);
   }
   *fd = Execlogin_StartLogin(&cmdvec);
   /*
     IF fd>=0 THEN
       ret:=tcgetattr(fd, tio);
       tio.c_lflag:=CAST(tcflag_t, CAST(BITSET, tio.c_lflag) + CAST(BITSET,
                ISIG));
       ret:=tcsetattr (fd, TCSAFLUSH, tio);
     END;
   */
   if (*fd>=2L && *fd<=255L) {
      if (verb) {
         osi_Werr("login fd=", 10ul);
         aprsstr_IntToStr(*fd, 1UL, argbuf, 8192ul);
         osi_Werr(argbuf, 8192ul);
         osi_Werr("\012", 2ul);
      }
   }
   else {
      if (verb) osi_Werr("login fd error\012", 16ul);
      *fd = -1L;
   }
   X2C_PFREE(l2adr);
} /* end Login() */


static char GetNum(const char h[], uint32_t h_len, char eot,
                 uint32_t * p, uint32_t * n)
{
   *n = 0UL;
   while ((uint8_t)h[*p]>='0' && (uint8_t)h[*p]<='9') {
      *n = ( *n*10UL+(uint32_t)(uint8_t)h[*p])-48UL;
      ++*p;
   }
   return h[*p]==eot;
} /* end GetNum() */


static void Parms(void)
{
   char err;
   char hh[1024];
   char h[1024];
   uint32_t l2parcnt;
   uint32_t p;
   uint32_t k;
   uint32_t j;
   uint32_t i0;
   struct frameio_UDPSOCK * anonym;
   err = 0;
   sockc = 0UL;
   for (i0 = 0UL; i0<=255UL; i0++) {
      numpar[i0].port = 0L;
   } /* end for */
   l2parcnt = 0UL;
   for (;;) {
      osi_NextArg(h, 1024ul);
      if (h[0U]==0) break;
      if (h[0U]=='-' && h[2U]==0) {
         if (h[1U]=='C') {
            /* -C ax25 destination */
            NextArgs(h, 1024ul);
            i0 = 0UL;
            j = 0UL;
            while (j<63UL && i0<aprsstr_Length(h, 1024ul)) {
               GetCall(h, 1024ul, &i0, (l2_pSTRING) &connectto0[j]);
               j += 7UL;
               if (j==7UL) j = 14UL;
            }
            connectto0[j] = 0;
         }
         else if (h[1U]=='c') {
            /* -c comand line */
            NextArgs(cmdline, 4096ul);
            keepconnected = 1;
         }
         else if (h[1U]=='p') {
            /* -p pipe */
            osi_NextArg(pipename, 4096ul);
         }
         else if (h[1U]=='i') {
            /* -i mycall */
            NextArgs(h, 1024ul);
            j = 7UL; /* first call is digi call, insert later */
            i0 = 0UL;
            do {
               GetCall(h, 1024ul, &i0, (l2_pSTRING) &mycall[j]);
               j += 7UL;
            } while (!(j>=112UL || h[i0]!=' '));
            if (j<=112UL) mycall[j] = 0;
            for (i0 = 0UL; i0<=6UL; i0++) {
               mycall[i0] = mycall[i0+7UL]; /* first mycall is digi call*/
            } /* end for */
         }
         else if (h[1U]=='j') {
            /* -j <num> convert cr lf to pipe */
            osi_NextArg(h, 1024ul);
            i0 = 0UL;
            if (!GetNum(h, 1024ul, 0, &i0, &convtopipe) || convtopipe>6UL) {
               err = 1;
               osi_WrStrLn("-j <0..6>", 10ul);
            }
         }
         else if (h[1U]=='m') {
            /* -j <num> convert cr lf to pipe */
            osi_NextArg(h, 1024ul);
            i0 = 0UL;
            if (!GetNum(h, 1024ul, 0, &i0, &montyp)) {
               err = 1;
               osi_WrStrLn("-m <0..1>", 10ul);
            }
         }
         else if (h[1U]=='D') {
            /* -D <n> discard frame len */
            osi_NextArg(h, 1024ul);
            i0 = 0UL;
            if (!GetNum(h, 1024ul, 0, &i0, &j)) {
               err = 1;
               osi_WrStrLn("-D <0..1500>", 13ul);
            }
            pppdiscard = (int32_t)j;
         }
         else if (h[1U]=='r') {
            /* -j <num> convert cr lf to pipe */
            osi_NextArg(h, 1024ul);
            i0 = 0UL;
            if (!GetNum(h, 1024ul, 0, &i0, &j) || j>4000000UL) {
               err = 1;
               osi_WrStrLn("-r <ms> (max 4000000)", 22ul);
            }
            else minrecontime = j*1000UL;
            keepconnected = 1;
         }
         else if (h[1U]=='R') {
            /* -r <ms> min reconn time */
            osi_NextArg(h, 1024ul);
            i0 = 0UL;
            if (!GetNum(h, 1024ul, 0, &i0, &j) || j>4000000UL) {
               err = 1;
               osi_WrStrLn("-R <ms> (max 4000000)", 22ul);
            }
            else {
               maxrecontime = j*1000UL;
            }
            keepconnected = 1;
         }
         else if (h[1U]=='n') {
            /* -R <ms> max reconn time */
            osi_NextArg(h, 1024ul);
            if (h[0U]=='a') p = 16UL;
            else {
               i0 = 0UL;
               if (!GetNum(h, 1024ul, 0, &i0, &p) || p>15UL) {
                  err = 1;
                  osi_WrStrLn("-n <port> <parameter> <value>", 30ul);
               }
            }
            osi_NextArg(h, 1024ul);
            switch ((unsigned)h[0U]) {
            case 't': /* txwait */
               j = 17UL;
               break;
            case 'd': /* dwait */
               j = 20UL;
               break;
            case 'o': /* maxframes */
               j = 23UL;
               break;
            case 'f': /* t1 */
               j = 24UL;
               break;
            case 'F': /* t3 */
               j = 25UL;
               break;
            case 'I': /* ipoll */
               j = 26UL;
               break;
            case 'n': /* retr */
               j = 27UL;
               break;
            case 'h': /* halfduplex */
               j = 24UL;
               break;
            default:;
               i0 = 0UL;
               if (!GetNum(h, 1024ul, 0, &i0, &j) || j>255UL) {
                  err = 1;
                  osi_WrStrLn("-n <port> <parameter> <value>", 30ul);
               }
               break;
            } /* end switch */
            osi_NextArg(h, 1024ul);
            i0 = 0UL;
            if (!GetNum(h, 1024ul, 0, &i0, &k)) {
               err = 1;
               osi_WrStrLn("-n <port> <parameter> <value>", 30ul);
            }
            if (!err) {
               if (l2parcnt>255UL) Error("parameter table full", 21ul);
               numpar[l2parcnt].port = (int32_t)p;
               numpar[l2parcnt].parmnum = (int32_t)j;
               numpar[l2parcnt].val = (int32_t)k;
               ++l2parcnt;
            }
         }
         else if (h[1U]=='U') {
            osi_NextArg(h, 1024ul);
            if (sockc>14UL) Error("too many ports", 15ul);
            { /* with */
               struct frameio_UDPSOCK * anonym = &frameio_udpsocks0[sockc];
               if (aprsstr_GetIp2(h, 1024ul, &anonym->ipnum, &anonym->toport,
                 &anonym->fromport, &anonym->checkip)<0L) {
                  Error("-U wrong ip:port:port number", 29ul);
               }
               anonym->fd = openudp();
               if (anonym->fd<0L || bindudp(anonym->fd,
                anonym->fromport)<0L) {
                  strncpy(h,"-U cannot open udp port ",1024u);
                  aprsstr_IntToStr((int32_t)anonym->fromport, 0UL, hh,
                1024ul);
                  aprsstr_Append(h, 1024ul, hh, 1024ul);
                  Error(h, 1024ul);
               }
            }
            ++sockc;
         }
         else if (h[1U]=='a') autodet = 1;
         else if (h[1U]=='P') {
            pppmode = 1;
            usedeflat = 1;
         }
         else if (h[1U]=='d') usedeflat = 1;
         else if (h[1U]=='v') {
            verb = 1;
         }
         else if (h[1U]=='V') {
            verb = 1;
            verb2 = 1;
         }
         else if (h[1U]=='l') convlfcr = 1UL;
         else if (h[1U]=='L') convlfcr = 2UL;
         else {
            if (h[1U]=='h') {
               osic_WrLn();
               osi_WrStrLn(" -a                                autodetect def\
late + ppp on first bytes", 75ul);
               osi_WrStrLn("                                     ppp + deflat\
e:\"!p\",deflate;\"!d\"", 69ul);
               osi_WrStrLn("                                     LF to CR:\"!\
l\", strip LF:\"!L\"", 66ul);
               osi_WrStrLn(" -C <destination>                  slave connect \
destination -C \"OE0AAA-12 OE5XBL\"", 83ul);
               osi_WrStrLn("                                     if no \"-C\"\
 run as server waiting for connect", 82ul);
               osi_WrStrLn(" -c <cmdline>                      execute comman\
d on connect -c \"/bin/bash bash %m %u\"", 88ul);
               osi_WrStrLn("                                     %m=mycall %u\
=usercall %U=same no SSID %%=%", 80ul);
               osi_WrStrLn("                                     if no \"-c\"\
 data path is stdin/stdout", 74ul);
               osi_WrStrLn(" -D <bytes>                        discard longer\
 ppp frames if pr tx buffer full", 82ul);
               osi_WrStrLn(" -d                                deflate on",
                46ul);
               osi_WrStrLn(" -h                                this", 40ul);
               osi_WrStrLn(" -i <mycall> <mycall> ...          connectable ca\
lls -i \"OE0AAA OE0AAA-15\"", 75ul);
               osi_WrStrLn("                                     first mycall\
 is digi and used to connect out", 82ul);
               osi_WrStrLn(" -j <1..6>                         1:del LF, 2:de\
l LF+CR>LF, 3:CR>LF, 4:del CR", 79ul);
               osi_WrStrLn("                                     5:del CR+LF>\
CR, 6:LF>CR in stream from pr", 79ul);
               osi_WrStrLn(" -l                                convert LF to \
CR in stream to pr", 68ul);
               osi_WrStrLn(" -L                                strip LF in st\
ream to pr", 60ul);
               osi_WrStrLn(" -m <0..2>                         ax25 Monitor t\
o stderr (2 with info)", 72ul);
               osi_WrStrLn(" -n <port> <parm> <value>          layer 2 parms,\
 \"-n a\" set on all ports", 74ul);
               osi_WrStrLn("                                     parm t:txwai\
t d:dwait o:maxfr f:t1 F:t3", 77ul);
               osi_WrStrLn("                                     parm I:ipoll\
 n:retr h:halfdup", 67ul);
               osi_WrStrLn(" -P                                ppp + deflate \
on", 52ul);
               osi_WrStrLn(" -p <filename>                     data to/from t\
his device/pipe -p /dev/ttyS0", 79ul);
               osi_WrStrLn(" -R <ms>                           enable slave \\
"keep connected\" with max reconnet delay", 89ul);
               osi_WrStr("                                     (max 4000000ms\
) (", 55ul);
               osic_WrINT32(maxrecontime/1000UL, 1UL);
               osi_WrStrLn(")", 2ul);
               osi_WrStrLn(" -r <ms>                           enable slave \\
"keep connected\" with min reconnet delay", 89ul);
               osi_WrStr("                                     and doubles de\
lay each conn try till \"-R\"  (", 82ul);
               osic_WrINT32(minrecontime/1000UL, 1UL);
               osi_WrStrLn(")", 2ul);
               osi_WrStrLn(" -U <x.x.x.x:destport:listenport>  axudp (autodet\
ect axudp2)", 61ul);
               osi_WrStrLn("                                     repeat -U fo\
r more ports", 62ul);
               osi_WrStrLn(" -v                                verbous",
                43ul);
               osi_WrStrLn(" -V                                more verbous",
                 48ul);
               osic_WrLn();
               X2C_ABORT();
            }
            if (h[1U]=='-') Error("- - ?", 6ul);
            if (h[1U]==0) Error("-  ?", 5ul);
            err = 1;
         }
      }
      else err = 1;
      if (err) break;
   }
   if (connectto0[0U]) {
      /* insert mycall as it is valid after whole cmd line */
      if (mycall[7U]==0) Error("need -i <call> for -C ", 23ul);
      X2C_MOVE((char *) &mycall[7U],(char *) &connectto0[7U],7UL);
   }
} /* end Parms() */


static void Connectto(void)
{
   pTASK ntask;
   struct l2_CONNECT connect;
   osic_alloc((char * *) &ntask, sizeof(struct TASK));
   if (ntask==0) return;
   memset((char *)ntask,(char)0,sizeof(struct TASK));
   ntask->infd = -1L;
   ntask->outfd = -1L;
   /*  GetAdress(link, ADR(ntask^.l2addr)); */
   memset((char *) &connect,(char)0,sizeof(struct l2_CONNECT));
   connect.port = 1U;
   connect.baud = 1U;
   connect.cpid = 240U;
   connect.l2adr = (l2_pSTRING)connectto0;
   connect.l3adr = 0;
   connect.typ = l2_cNORMAL;
   ntask->link0 = l2_Connect0((char *)ntask, &connect);
   ntask->state = 5U;
   ntask->next = tasks;
   ntask->crlfmode = convlfcr;
   tasks = ntask;
} /* end Connectto() */

static void killdisc(int32_t);


static void killdisc(int32_t signum)
{
   if (verb) osi_Werr("sigint\012", 8ul);
   /*  IF (tasks<>NIL) & (tasks^.state=2) THEN WrStrLn("disc!");
                Disconnect(tasks^.link, TRUE); */
   /*  ELSE HALT(signum) END; */
   if (terminate) X2C_ABORT();
   terminate = 1;
} /* end killdisc() */


static void initcompress(pTASK pt0)
{
   /*IntToStr(SIZE(pt^.pcdeflat^), 1, deb); Werr(deb);Werr("=mem"); */
   osic_alloc((char * *) &pt0->pcdeflat, sizeof(struct DEFLATBUF));
   if (pt0->pcdeflat==0) Error("alloc compres memory", 21ul);
   deflate_Initdeflate(&pt0->pcdeflat->deflatcontext);
   deflate_Initexpand(&pt0->pcdeflat->expandcontext);
   pt0->pcdeflat->outlen = 0L;
   pt0->pcdeflat->xoutlen = 0L;
   pt0->pcdeflat->inp0 = 0L;
   pt0->pcdeflat->xinp = 0L;
   pt0->pcdeflat->done = 0;
   if (pppmode) pt0->pcdeflat->pppframing = 1;
   pt0->pcdeflat->pppstate = 0;
   if (verb) osi_Werr("init compression\012", 18ul);
} /* end initcompress() */


static char pppcrc(char b[], uint32_t b_len, int32_t len)
{
   char crc2;
   char crc1;
   char ok0;
   /*ShowHex(b, len); */
   if (len<2L) return 0;
   len -= 2L;
   crc1 = b[len];
   crc2 = b[len+1L];
   aprsstr_AppCRC(b, b_len, len);
   ok0 = crc1==b[len] && crc2==b[len+1L];
   if (!ok0) osi_Werr(" crc-err \012", 11ul);
   return ok0;
} /* end pppcrc() */


static void compress(pTASK pt0)
{
   int32_t i0;
   struct TASK * anonym;
   int32_t tmp;
   { /* with */
      struct TASK * anonym = pt0;
      if (anonym->pcdeflat->pppframing) {
         for (;;) {
            if (anonym->pcdeflat->inp0>=anonym->inlen) {
               /* out of data */
               anonym->pcdeflat->inp0 = 0L;
               anonym->inlen = 0L;
               break;
            }
            /*IF verb2 THEN WrHex(ORD(inbuf[pcdeflat^.inp]), 3) END; */
            if (anonym->pcdeflat->pppstate==0 && anonym->inbuf[anonym->pcdeflat->inp0]
                =='~') {
               /* hunt mode */
               anonym->pcdeflat->pppstate = '\001'; /* in frame */
               anonym->pcdeflat->ppplen = 0L;
            }
            else if (anonym->pcdeflat->pppstate=='\001') {
               if (anonym->inbuf[anonym->pcdeflat->inp0]=='}') {
                  anonym->pcdeflat->pppstate = '\002';
                /* escaped char follows */
               }
               else {
                  if (anonym->inbuf[anonym->pcdeflat->inp0]=='~') {
                     /* frame complete */
                     if (anonym->pcdeflat->ppplen>0L) {
                        if (pppcrc(anonym->pcdeflat->pppbuf, 4096ul,
                anonym->pcdeflat->ppplen)) {
                           /* frame good */
                           /*IntToStr(pcdeflat^.ppplen, 7, deb);Werr(deb);
                Werr("=c "); */
                           if (anonym->pcdeflat->ppplen<=pppdiscard || l2_SendStr(anonym->link0,
                 0U, 0)) {
                              tmp = anonym->pcdeflat->ppplen-1L;
                              i0 = 0L;
                              if (i0<=tmp) for (;; i0++) {
                                 deflate_Deflatbyte(&anonym->pcdeflat->deflatcontext,
                 anonym->pcdeflat->pppbuf[i0], 0, anonym->pcdeflat->outbuf,
                20001ul, &anonym->pcdeflat->outlen);
                                 if (i0==tmp) break;
                              } /* end for */
                              deflate_Deflatbyte(&anonym->pcdeflat->deflatcontext,
                 0, '\002', anonym->pcdeflat->outbuf, 20001ul,
                &anonym->pcdeflat->outlen);
                           }
                           else if (verb) {
                              osi_Werr("ppp frame discarded\012", 21ul);
                           }
                        }
                        else osi_Werr("ppp frame crc error\012", 21ul);
                        /*IntToStr(pcdeflat^.outlen, 7, deb);Werr(deb);
                Werr("=co "); */
                        anonym->pcdeflat->ppplen = 0L;
                     }
                     ++anonym->pcdeflat->inp0;
                     break;
                  }
                  anonym->pcdeflat->pppbuf[anonym->pcdeflat->ppplen]
                = anonym->inbuf[anonym->pcdeflat->inp0];
                  if (anonym->pcdeflat->ppplen<4095L) {
                     ++anonym->pcdeflat->ppplen;
                  }
               }
            }
            else if (anonym->pcdeflat->pppstate=='\002') {
               /* stuffed byte */
               anonym->pcdeflat->pppbuf[anonym->pcdeflat->ppplen]
                = (char)((uint8_t)(uint8_t)
                anonym->inbuf[anonym->pcdeflat->inp0]^0x20U);
               if (anonym->pcdeflat->ppplen<4095L) {
                  ++anonym->pcdeflat->ppplen;
               }
               anonym->pcdeflat->pppstate = '\001';
            }
            ++anonym->pcdeflat->inp0;
         }
      }
      else {
         while (anonym->pcdeflat->inp0<anonym->inlen && anonym->pcdeflat->outlen<3601L)
                 {
            deflate_Deflatbyte(&anonym->pcdeflat->deflatcontext,
                anonym->inbuf[anonym->pcdeflat->inp0], 0,
                anonym->pcdeflat->outbuf, 20001ul,
                &anonym->pcdeflat->outlen);
            ++anonym->pcdeflat->inp0;
         }
         if (anonym->pcdeflat->inp0>=anonym->inlen) {
            /* inbuffer empty so send compressed block */
            deflate_Deflatbyte(&anonym->pcdeflat->deflatcontext, 0, '\002',
                anonym->pcdeflat->outbuf, 20001ul,
                &anonym->pcdeflat->outlen); /* flush */
            anonym->pcdeflat->inp0 = 0L;
            anonym->inlen = 0L;
         }
      }
   }
/*ShowHex(pcdeflat^.outbuf, pcdeflat^.outlen); WrStrLn(""); */
} /* end compress() */


static void expand(pTASK pt0)
{
   char done;
   char pb[3001];
   int32_t j;
   int32_t i0;
   struct TASK * anonym;
   /*ShowHex(outbuf, outlen); */
   int32_t tmp;
   { /* with */
      struct TASK * anonym = pt0;
      for (;;) {
         /*IF pcdeflat^.xoutlen>=SIZE(pcdeflat^.xoutbuf)
                -300 THEN Werr(" outfull ") END; */
         if ((anonym->pcdeflat->xinp>=anonym->outlen || anonym->pcdeflat->xoutlen>=3796L)
                 || anonym->pcdeflat->xoutlen<0L) break;
         /*WrHex(ORD(outbuf[pcdeflat^.xinp]), 3);INC(deb1); */
         deflate_Expandbyte(&anonym->pcdeflat->expandcontext,
                anonym->outbuf[anonym->pcdeflat->xinp],
                anonym->pcdeflat->xoutbuf, 4096ul,
                &anonym->pcdeflat->xoutlen, &done);
         /*IF pcdeflat^.xoutlen<0 THEN Werr(" expand error"+NL) END; */
         /*Werr("(");IntToStr(pcdeflat^.xoutlen, 1, deb);Werr(deb);Werr(")");
                 */
         ++anonym->pcdeflat->xinp;
         if (done && anonym->pcdeflat->pppframing) {
            /* rebuild ppp frame */
            /*IntToStr(deb1, 7, deb);Werr(deb);Werr("=xi ");deb1:=0; */
            /*IntToStr(pcdeflat^.xoutlen, 7, deb);Werr(deb);Werr("=x "); */
            /*WrStrLn("done");ShowHex(pcdeflat^.xoutbuf, pcdeflat^.xoutlen);
                */
            if (pppcrc(anonym->pcdeflat->xoutbuf, 4096ul,
                anonym->pcdeflat->xoutlen)) {
               /*WrStrLn("");WrStr("Y:");
                ShowHex(pcdeflat^.xoutbuf, pcdeflat^.xoutlen); WrStrLn("");
                */
               pb[0U] = '~';
               j = 1L;
               tmp = anonym->pcdeflat->xoutlen-1L;
               i0 = 0L;
               if (i0<=tmp) for (;; i0++) {
                  if ((anonym->pcdeflat->xoutbuf[i0]
                =='~' || anonym->pcdeflat->xoutbuf[i0]=='}') || (uint8_t)
                anonym->pcdeflat->xoutbuf[i0]<' ') {
                     /* stuff */
                     pb[j] = '}';
                     if (j<3000L) ++j;
                     pb[j] = (char)((uint8_t)(uint8_t)
                anonym->pcdeflat->xoutbuf[i0]^0x20U);
                  }
                  else pb[j] = anonym->pcdeflat->xoutbuf[i0];
                  if (j<3000L) ++j;
                  if (i0==tmp) break;
               } /* end for */
               pb[j] = '~';
               ++j;
               i0 = 0L;
               while (i0<4095L && i0<j) {
                  anonym->pcdeflat->xoutbuf[i0] = pb[i0];
                  ++i0;
               }
               anonym->pcdeflat->xoutlen = i0;
               anonym->pcdeflat->done = 1;
               /*WrStrLn("");WrStr("X:");
                ShowHex(pcdeflat^.xoutbuf, pcdeflat^.xoutlen); WrStrLn("");
                */
               break;
            }
            anonym->pcdeflat->xoutlen = -1L; /* crc error */
         }
      }
      if (anonym->pcdeflat->xinp>=anonym->outlen) {
         /* outbuffer empty */
         anonym->pcdeflat->xinp = 0L;
         anonym->outlen = 0L;
      }
   }
} /* end expand() */


static void wrpipe(int32_t fd, char b[], uint32_t b_len,
                int32_t * blen)
{
   int32_t i0;
   int32_t len;
   int32_t tmp;
   /*WrStrLn("");WrStr("P:"); ShowHex(b, blen); WrStrLn(""); */
   len = write(fd, (char *)b, (uint32_t)*blen);
   if (len>*blen) len = *blen;
   if (len>0L) {
      *blen -= len;
      tmp = *blen-1L;
      i0 = 0L;
      if (i0<=tmp) for (;; i0++) {
         b[i0] = b[i0+len];
         if (i0==tmp) break;
      } /* end for */
   }
/*    IF blen>0 THEN MOVE(ADR(b[len]), ADR(b), blen) END; */
} /* end wrpipe() */


static void sendjunk(l2_pLINK link0, struct JUNKBUF * junk)
{
   if (junk->len==256L || junk->len>=0L && junk->time0<=0L) {
      if (l2_SendStr(link0, (uint16_t)junk->len, (l2_pSTRING)junk->buf)) {
         junk->len = 0L;
      }
   }
} /* end sendjunk() */


static void wrl2(l2_pLINK link0, char b[], uint32_t b_len,
                int32_t * blen, struct JUNKBUF * junk)
{
   int32_t j;
   int32_t i0;
   int32_t len;
   /*WrStr("L:"); WrInt(blen, 1); WrStr(" "); */
   if (*blen==0L) return;
   if (junk->len<256L) {
      len = 0L;
      while (junk->len<256L && len<*blen) {
         junk->buf[junk->len] = b[len];
         ++junk->len;
         ++len;
      }
      j = len;
      i0 = 0L;
      while (j<*blen) {
         b[i0] = b[j];
         ++j;
         ++i0;
      }
      *blen -= len;
      junk->time0 = 4L;
   }
   sendjunk(link0, junk);
} /* end wrl2() */


static void wrclose(pTASK pt0, int32_t fd, char c[], uint32_t c_len)
{
   char s[100];
   X2C_PCOPY((void **)&c,c_len);
   WerrCall(pt0);
   aprsstr_IntToStr(fd, 1UL, s, 100ul);
   osi_Werr(c, c_len);
   osi_Werr(s, 100ul);
   osi_Werr("\012", 2ul);
   X2C_PFREE(c);
} /* end wrclose() */


static void crlf(char b[], uint32_t b_len, int32_t * len,
                uint32_t mode)
{
   int32_t j;
   int32_t i0;
   if (mode>0UL) {
      /* convert LF to pr */
      i0 = 0L;
      j = 0L;
      while (i0<*len) {
         if (b[i0]=='\012') {
            if (mode==1UL) {
               b[j] = '\015';
               ++j;
            }
         }
         else {
            b[j] = b[i0];
            ++j;
         }
         ++i0;
      }
      *len = j;
   }
} /* end crlf() */


static void crlfp(char b[], uint32_t b_len, int32_t * len,
                uint32_t mode)
/*1 del lf, 2 del lf cr>lf, 3 cr>lf, 4 del cr, 5 del cr lf>cr, 6 lf>cr */
{
   int32_t j;
   int32_t i0;
   if (mode>0UL) {
      /* convert LF to pr */
      i0 = 0L;
      j = 0L;
      while (i0<*len) {
         if (mode==1UL) {
            if (b[i0]!='\012') {
               b[j] = b[i0];
               ++j;
            }
         }
         else if (mode==2UL) {
            if (b[i0]=='\015') {
               b[j] = '\012';
               ++j;
            }
            else if (b[i0]!='\012') {
               b[j] = b[i0];
               ++j;
            }
         }
         else if (mode==3UL) {
            if (b[i0]=='\015') b[j] = '\012';
            else b[j] = b[i0];
            ++j;
         }
         else if (mode==4UL) {
            if (b[i0]!='\015') {
               b[j] = b[i0];
               ++j;
            }
         }
         else if (mode==5UL) {
            if (b[i0]=='\012') {
               b[j] = '\015';
               ++j;
            }
            else if (b[i0]!='\015') {
               b[j] = b[i0];
               ++j;
            }
         }
         else if (mode==6UL) {
            if (b[i0]=='\012') b[j] = '\015';
            else b[j] = b[i0];
            ++j;
         }
         ++i0;
      }
      *len = j;
   }
} /* end crlfp() */


static void detect(pTASK pt0)
/* startet task first sends command on stdout */
{
   int32_t j;
   int32_t i0;
   struct TASK * anonym;
   { /* with */
      struct TASK * anonym = pt0;
      while (anonym->inlen>0L && anonym->detectdone>0UL) {
         i0 = 0L;
         if (anonym->detectdone==1UL) {
            if (anonym->inbuf[0U]=='!') {
               anonym->detectdone = 2UL;
               i0 = 1L;
            }
            else anonym->detectdone = 0UL;
         }
         else if (anonym->detectdone==2UL) {
            if (anonym->inbuf[0U]=='d') {
               initcompress(pt0);
               i0 = 1L;
               if (verb) osi_Werr("task switched deflate on\012", 26ul);
            }
            else if (anonym->inbuf[0U]=='p') {
               initcompress(pt0);
               anonym->pcdeflat->pppframing = 1;
               i0 = 1L;
               if (verb) {
                  osi_Werr("task switched ppp + deflate on\012", 32ul);
               }
            }
            else if (anonym->inbuf[0U]=='l') {
               anonym->crlfmode = 1UL;
               i0 = 1L;
               if (verb) osi_Werr("task switched LF to CR on\012", 27ul);
            }
            else if (anonym->inbuf[0U]=='L') {
               anonym->crlfmode = 2UL;
               i0 = 1L;
               if (verb) osi_Werr("task switched delete LF on\012", 28ul);
            }
            anonym->detectdone = 0UL;
         }
         else anonym->detectdone = 0UL;
         if (i0>0L) {
            /* delete command */
            j = 0L;
            while (i0<anonym->inlen) {
               anonym->inbuf[j] = anonym->inbuf[i0];
               ++i0;
               ++j;
            }
            anonym->inlen = j;
         }
      }
   }
} /* end detect() */


static void AppendTime(char s[], uint32_t s_len)
{
   char h[31];
   aprsstr_TimeToStr(osic_time()%86400UL, h, 31ul);
   aprsstr_Append(s, s_len, " - ", 4ul);
   aprsstr_Append(s, s_len, h, 31ul);
} /* end AppendTime() */

static uint16_t l2cat_UA = 0x63U;

static uint16_t l2cat_DM = 0xFU;

static uint16_t l2cat_SABM = 0x2FU;

static uint16_t l2cat_DISC = 0x43U;

static uint16_t l2cat_FRMR = 0x87U;

static uint16_t l2cat_UI = 0x3U;

static uint16_t l2cat_RR = 0x1U;

static uint16_t l2cat_REJ = 0x9U;

static uint16_t l2cat_RNR = 0x5U;

#define l2cat_LF0 "\015"

#define l2cat_LINELEN 78

static char l2cat_oneline = 0;


static void Nibble(int32_t * p, char rxbuf[2048], uint16_t n)
{
   rxbuf[*p] = (char)((uint32_t)(48U+n)+7UL*(uint32_t)(n>9U));
   ++*p;
} /* end Nibble() */


static void WriteHex(char rxbuf[2048], int32_t * p, char c)
{
   Nibble(p, rxbuf, (uint16_t)((uint32_t)(uint8_t)c/16UL));
   Nibble(p, rxbuf, (uint16_t)((uint32_t)(uint8_t)c&15UL));
} /* end WriteHex() */


static void ShowCall(int32_t * p, char rxbuf[2048],
                struct l2_GETADRESS * getadress, uint16_t cp,
                char hbit)
{
   uint32_t i0;
   char c;
   uint32_t tmp;
   tmp = (uint32_t)(cp+5U);
   i0 = (uint32_t)cp;
   if (i0<=tmp) for (;; i0++) {
      c = getadress->adress[i0];
      if (c!=' ') {
         if ((uint8_t)c>' ') {
            rxbuf[*p] = c;
            ++*p;
         }
         else WriteHex(rxbuf, p, c);
      }
      if (i0==tmp) break;
   } /* end for */
   i0 = (uint32_t)(uint8_t)getadress->adress[cp+6U]&15UL;
   if (i0) {
      rxbuf[*p] = '-';
      ++*p;
      if (i0>9UL) {
         rxbuf[*p] = (char)(48UL+i0/10UL);
         ++*p;
      }
      rxbuf[*p] = (char)(48UL+i0%10UL);
      ++*p;
   }
   if (hbit && (0x40U & (uint16_t)(uint32_t)(uint8_t)
                getadress->adress[cp+6U])) {
      rxbuf[*p] = '*';
      ++*p;
   }
} /* end ShowCall() */


static void MonHead(char rxbuf[2048], struct l2_GETADRESS * getadress,
                l2_pLINK l, int32_t ilen, const char info[],
                uint32_t info_len)
{
   int32_t p;
   int32_t i0;
   uint32_t com;
   uint16_t cmd;
   char cr;
   char pf;
   char PF0[4];
   char s[21];
   /*    udp2:ARRAY[0..99] OF CHAR; */
   int32_t tmp;
   l2_GetAdress0(l, getadress);
   aprsstr_IntToStr((int32_t)getadress->port, 1UL, rxbuf, 2048ul);
   aprsstr_Append(rxbuf, 2048ul, ":fm ", 5ul);
   p = (int32_t)aprsstr_Length(rxbuf, 2048ul);
   ShowCall(&p, rxbuf, getadress, 7U, 0);
   rxbuf[p] = ' ';
   ++p;
   rxbuf[p] = 't';
   ++p;
   rxbuf[p] = 'o';
   ++p;
   rxbuf[p] = ' ';
   ++p;
   ShowCall(&p, rxbuf, getadress, 0U, 0);
   rxbuf[p] = ' ';
   ++p;
   i0 = 14L;
   while (i0+7L<71L && getadress->adress[i0]) {
      if (i0==14L) {
         rxbuf[p] = 'v';
         ++p;
         rxbuf[p] = 'i';
         ++p;
         rxbuf[p] = 'a';
         ++p;
         rxbuf[p] = ' ';
         ++p;
      }
      ShowCall(&p, rxbuf, getadress, (uint16_t)i0, 1);
      rxbuf[p] = ' ';
      ++p;
      i0 += 7L;
   }
   rxbuf[p] = 'c';
   ++p;
   rxbuf[p] = 't';
   ++p;
   rxbuf[p] = 'l';
   ++p;
   rxbuf[p] = ' ';
   ++p;
   cmd = (uint16_t)getadress->my;
   com = (uint32_t)((0x40U & (uint16_t)(uint32_t)(uint8_t)
                getadress->adress[6U])!=0)+2UL*(uint32_t)
                ((0x40U & (uint16_t)(uint32_t)(uint8_t)
                getadress->adress[13U])!=0);
   pf = (0x10U & cmd)!=0;
   cmd = cmd&~0x10U;
   if ((cmd&0xFU)==0x1U) {
      strncpy(PF0,"RR  ",4u);
      PF0[2U] = (char)(48U+(uint16_t)cmd/32U);
   }
   else if ((cmd&0xFU)==0x5U) {
      strncpy(PF0,"RNR ",4u);
      PF0[3U] = (char)(48U+(uint16_t)cmd/32U);
   }
   else if ((cmd&0xFU)==0x9U) {
      strncpy(PF0,"REJ ",4u);
      PF0[3U] = (char)(48U+(uint16_t)cmd/32U);
   }
   else if ((cmd&0x1U)==0U) {
      strncpy(PF0,"I   ",4u);
      PF0[2U] = (char)(48U+((uint16_t)cmd/2U&7U));
      PF0[1U] = (char)(48U+(uint16_t)cmd/32U);
   }
   else if (cmd==0x3U) strncpy(PF0,"UI  ",4u);
   else if (cmd==0xFU) strncpy(PF0,"DM  ",4u);
   else if (cmd==0x2FU) strncpy(PF0,"SABM",4u);
   else if (cmd==0x43U) strncpy(PF0,"DISC",4u);
   else if (cmd==0x63U) strncpy(PF0,"UA  ",4u);
   else if (cmd==0x87U) strncpy(PF0,"FRMR",4u);
   else WriteHex(rxbuf, &p, (char)getadress->my);
   for (i0 = 0L; i0<=3L; i0++) {
      if (PF0[i0]!=' ') {
         rxbuf[p] = PF0[i0];
         ++p;
      }
   } /* end for */
   strncpy(PF0,"v^-+",4u);
   cr = 1;
   if (com==0UL || com==3UL) {
      rxbuf[p] = 'v';
      ++p;
      rxbuf[p] = '1';
      ++p;
   }
   else {
      rxbuf[p] = PF0[(com&1UL)+2UL*(uint32_t)pf];
      ++p;
   }
   if (getadress->cpid<=255U) {
      rxbuf[p] = ' ';
      ++p;
      rxbuf[p] = 'p';
      ++p;
      rxbuf[p] = 'i';
      ++p;
      rxbuf[p] = 'd';
      ++p;
      rxbuf[p] = ' ';
      ++p;
      WriteHex(rxbuf, &p, (char)getadress->cpid);
   }
   if ((0x10U & (uint16_t)(uint32_t)(uint8_t)getadress->adress[13U])
                ==0) {
      rxbuf[p] = ' ';
      ++p;
      rxbuf[p] = 'd';
      ++p;
      rxbuf[p] = 'a';
      ++p;
      rxbuf[p] = 'm';
      ++p;
      rxbuf[p] = 'a';
      ++p;
      rxbuf[p] = ' ';
      ++p;
   }
   /*
     Getudp2info(l, udp2);
     IF udp2[0]<>0C THEN
       rxbuf[p]:=" ";
       INC(p);
       i:=0;
       WHILE (p<HIGH(rxbuf)) & (udp2[i]<>0C) DO rxbuf[p]:=udp2[i]; INC(p);
                INC(i) END;
     END;
   */
   rxbuf[p] = 0;
   if (ilen>0L && montyp==1UL) {
      aprsstr_IntToStr(ilen, 1UL, s, 21ul);
      aprsstr_Append(rxbuf, 2048ul, " ", 2ul);
      aprsstr_Append(rxbuf, 2048ul, s, 21ul);
      aprsstr_Append(rxbuf, 2048ul, "B", 2ul);
   }
   AppendTime(rxbuf, 2048ul);
   if (ilen>0L && montyp>1UL) {
      p = (int32_t)aprsstr_Length(rxbuf, 2048ul);
      rxbuf[p] = '\012';
      ++p;
      tmp = ilen-1L;
      i0 = 0L;
      if (i0<=tmp) for (;; i0++) {
         if ((uint8_t)info[i0]>=' ' && (uint8_t)info[i0]<'\177') {
            rxbuf[p] = info[i0];
         }
         else rxbuf[p] = '.';
         ++p;
         if (i0==tmp) break;
      } /* end for */
      rxbuf[p] = 0;
   }
   osi_Werr(rxbuf, 2048ul);
   osi_Werr("\012", 2ul);
} /* end MonHead() */


static void Monitor(void)
{
   char rxbuf[2048];
   struct l2_GETADRESS getadress;
   int32_t ilen;
   char ibuf[300];
   if (pmon==0) pmon = l2_GetMon();
   if (pmon) {
      ilen = (int32_t)l2_GetStr(pmon, 256U, (l2_pSTRING)ibuf);
      MonHead(rxbuf, &getadress, pmon, ilen, ibuf, 300ul);
      l2_Disconnect(&pmon, 1); /*release buffer*/
   }
} /* end Monitor() */


static void sleeprecon(void)
{
   char s[21];
   if (!terminate) {
      if (verb) {
         aprsstr_IntToStr((int32_t)(recontime/1000UL), 1UL, s, 21ul);
         osi_Werr(" delay ", 8ul);
         osi_Werr(s, 21ul);
         osi_Werr("ms before next connect\012", 24ul);
      }
      usleep(recontime);
   }
   if (recontime>maxrecontime/2UL) recontime = maxrecontime;
   else recontime = recontime*2UL;
} /* end sleeprecon() */

static uint32_t i;

static uint32_t ts;

static uint32_t timeout;

static int32_t ret;

static l2_CALLBACKPROC eventproc;

static pTASK pt;

static pTASK pth;

static int32_t pp0;

static int32_t pp1;

static uint16_t sockset;


X2C_STACK_LIMIT(100000l)
extern int main(int argc, char **argv)
{
   uint32_t tmp;
   X2C_BEGIN(&argc,argv,1,4000000l,8000000l);
   if (sizeof(IOBUF)!=256) X2C_ASSERT(0);
   deflate_BEGIN();
   frameio_BEGIN();
   l2_BEGIN();
   osi_BEGIN();
   aprsstr_BEGIN();
   Execlogin_BEGIN();
   memset((char *)frameio_udpsocks0,(char)0,
                sizeof(struct frameio_UDPSOCK [15]));
   cmdline[0] = 0;
   pipename[0] = 0;
   connectto0[0] = 0;
   memset((char *)mycall,(char)0,113UL);
   convlfcr = 0UL;
   convtopipe = 0UL;
   terminate = 0;
   keepconnected = 0;
   usedeflat = 0;
   verb2 = 0;
   verb = 0;
   pppmode = 0;
   autodet = 0;
   pppdiscard = X2C_max_longint;
   montyp = 0UL;
   minrecontime = 2000000UL;
   maxrecontime = 120000000UL;
   Parms();
   pmon = 0;
   eventproc = Event;
   sockset = 0U;
   tmp = sockc;
   i = 0UL;
   if (i<=tmp) for (;; i++) {
      sockset |= (1U<<i);
      if (i==tmp) break;
   } /* end for */
   l2_L2Init(1000U, sockset, eventproc);
   signal(SIGCHLD, cleanup); /*CleanWho*/
   /*
     MOVE(ADR(mycall), ADR(hc[0*7]), 7);          (* via call *)
     MOVE(ADR(mycall), ADR(hc[1*7]), 7);          (* connect call *)
     hc[2*7]:=0C;
   */
   parms.test = 0;
   parms.parm = 0U; /* mycalls */
   parms.port = 1U;
   parms.str = (l2_pSTRING)mycall;
   l2_Parm(&parms);
   for (i = 0UL; i<=255UL; i++) {
      parms.test = 0;
      parms.port = (uint16_t)i;
      parms.parm = 10U;
      parms.val = montyp;
      l2_Parm(&parms);
      if (numpar[i].port>0L) {
         /* port configured */
         pp0 = numpar[i].port;
         pp1 = pp0;
         if (pp0>15L) {
            /* set parameter to all ports */
            pp0 = 1L;
            pp1 = 15L;
         }
         do {
            parms.test = 0;
            parms.port = (uint16_t)pp0;
            parms.parm = (uint16_t)numpar[i].parmnum;
            parms.val = (uint32_t)numpar[i].val;
            if (verb2) {
               osi_WrStr("set port ", 10ul);
               osic_WrINT32((uint32_t)parms.port, 1UL);
               osi_WrStr(" parmnum ", 10ul);
               osic_WrINT32((uint32_t)parms.parm, 1UL);
               osi_WrStr(" to value ", 11ul);
               osic_WrINT32(parms.val, 1UL);
               osi_WrStrLn("", 1ul);
            }
            l2_Parm(&parms);
            ++pp0;
         } while (pp0<=pp1);
      }
   } /* end for */
   if (connectto0[0U]) {
      signal(SIGTERM, killdisc);
      signal(SIGINT, killdisc);
      signal(SIGPIPE, killdisc);
   }
   recontime = minrecontime;
   tasks = 0;
   timesum = 0L;
   for (;;) {
      if (tasks==0) {
         if (terminate) X2C_ABORT();
         if (connectto0[0U]) {
            for (;;) {
               Connectto();
               if (terminate || tasks) break;
               sleeprecon();
            }
         }
      }
      fdclr();
      pt = tasks;
      while (pt) {
         if (pt->state==1U) {
            /*        recontime:=minrecontime; */
            if (cmdline[0U]) {
               /* start task */
               Login(&pt->infd, pt->l2addr.adress, 72ul);
               pt->outfd = pt->infd;
               /*WrInt(pt^.fd, 1); WrStrLn(" fd"); */
               if (pt->infd>=0L) {
                  /*ioctl(pt^.fd, TIOCSCTTY, ADR(iocbuf));  */
                  pt->state = 2U;
               }
               else {
                  pt->state = 3U;
                  if (verb) osi_Werr("task start failed\012", 19ul);
               }
            }
            else if (pipename[0U]) {
               /* use pipe */
               pt->infd = osi_OpenRW(pipename, 4096ul);
               pt->outfd = pt->infd;
               if (pt->infd>=0L) pt->state = 2U;
               else {
                  pt->state = 3U;
                  if (verb) osi_Werr("pipe open error\012", 17ul);
               }
            }
            else {
               pt->state = 2U; /* use stdin stdout */
               pt->infd = 0L;
               pt->outfd = 1L;
            }
            if (usedeflat) initcompress(pt);
            else pt->pcdeflat = 0;
         }
         else if (pt->state==2U) {
            if ((terminate || (0x2U & pt->events)) || (0x4U & pt->events)) {
               pt->state = 3U;
               if (verb) {
                  WerrCall(pt);
                  osi_Werr(" layer2 disconnect\012", 20ul);
               }
               if (cmdline[0U]==0 && connectto0[0U]==0) X2C_ABORT();
            }
            if ((pt->infd>=0L && pt->inlen==0L) && pt->inignor==0UL) {
               fdsetr((uint32_t)pt->infd);
            }
            if (pt->inignor>0UL) --pt->inignor;
         }
         else if (pt->state==3U) {
            if (verb) {
               WerrCall(pt);
               osi_Werr(" disconnect\012", 13ul);
            }
            if (cmdline[0U]) {
               if (pt->infd>=0L && pt->infd!=pt->outfd) {
                  if (verb) wrclose(pt, pt->infd, " close in pipe ", 16ul);
                  osic_Close(pt->infd);
               }
               if (pt->outfd>=0L) {
                  if (verb) wrclose(pt, pt->outfd, " close out pipe ", 17ul);
                  osic_Close(pt->outfd);
               }
               pt->infd = -1L;
               pt->outfd = -1L;
               fdclr();
            }
            l2_Disconnect(&pt->link0, 0);
            pt->state = 4U;
         }
         if (pt->state==4U || pt->state==5U) {
            if ((terminate || (0x2U & pt->events)) || (0x4U & pt->events)) {
               if (verb) {
                  WerrCall(pt);
                  osi_Werr(" deallocate link\012", 18ul);
               }
               l2_Disconnect(&pt->link0, 1);
               if (verb) {
                  WerrCall(pt);
                  osi_Werr(" disconnect close layer2\012", 26ul);
               }
               if (tasks==pt) tasks = pt->next;
               else {
                  pth = tasks;
                  while (pth->next!=pt) pth = pth->next;
                  pth->next = pt->next;
               }
               if (pt->pcdeflat) {
                  osic_free((char * *) &pt->pcdeflat,
                sizeof(struct DEFLATBUF));
               }
               osic_free((char * *) &pt, sizeof(struct TASK));
               pt = 0;
               if (tasks==0) {
                  if (!keepconnected) terminate = 1;
                  else if (connectto0[0U]) sleeprecon();
               }
            }
            else pt = pt->next;
         }
         else pt = pt->next;
      }
      ts = 0UL;
      timeout = 10000UL;
      ret = selectrwt(&ts, &timeout);
      timesum += (int32_t)(10000UL-timeout);
      if (timesum>=10000L) {
         /*    IF timeout<=0 THEN */
         l2_Layer2();
         if (montyp>0UL) Monitor();
         timesum -= 10000L;
      }
      pt = tasks;
      while (pt) {
         if (pt->state==2U && pt->infd>=0L) {
            /*WrInt(pt^.state, 1); WrStrLn(" st"); */
            if (issetr((uint32_t)pt->infd)) {
               if (pt->inlen==0L) {
                  if (!terminate) {
                     /* read blocks after a SIGINT - why ever */
                     pt->inlen = osi_RdBin(pt->infd, (char *)pt->inbuf,
                256u/1u, 256UL);
                     if (pt->inlen==0L) {
                        pt->inignor = 20UL; /* read from pipe why ever */
                     }
                  }
               }
               if (verb2 && pt->inlen>0L) {
                  Whex("IN ", 4ul, pt->inbuf, 256ul, pt->inlen);
               }
            }
            /*WrStrLn("");WrStr("I:"); ShowHex(pt^.inbuf, pt^.inlen); */
            if (pt->inlen>0L) {
               if (~pt->detectdone>0UL) {
                  detect(pt); /* switch option with first bytes in stdin */
               }
               if (pt->pcdeflat) {
                  /* compress */
                  if (pt->pcdeflat->outlen==0L) compress(pt);
                  wrl2(pt->link0, pt->pcdeflat->outbuf, 20001ul,
                &pt->pcdeflat->outlen, &pt->junkbuf);
               }
               else {
                  crlf(pt->inbuf, 256ul, &pt->inlen, pt->crlfmode);
                  wrl2(pt->link0, pt->inbuf, 256ul, &pt->inlen,
                &pt->junkbuf);
               }
            }
            else if (pt->inlen<0L) {
               if (pt->junkbuf.len>0L) {
                  /* send rest */
                  pt->junkbuf.time0 = 0L;
                  sendjunk(pt->link0, &pt->junkbuf);
               }
               else {
                  pt->state = 3U;
                  if (verb) osi_Werr("read pipe from task broken\012", 28ul);
               }
            }
            if (pt->state==2U) {
               for (;;) {
                  if (pt->outlen>0L) {
                     if (verb2) {
                        Whex("OUT ", 5ul, pt->outbuf, 256ul, pt->outlen);
                     }
                     if (pt->pcdeflat) {
                        /* decompress */
                        if (!pt->pcdeflat->done) {
                           expand(pt);
                           if (pt->pcdeflat->xoutlen<0L) {
                              /* expand or crc error */
                              pt->state = 3U;
                              if (verb) {
                                 osi_Werr("deflate or crc error\012", 22ul);
                              }
                              break;
                           }
                        }
                        if (!pt->pcdeflat->pppframing || pt->pcdeflat->done) {
                           if (pt->pcdeflat->xoutlen>0L) {
                              wrpipe(pt->outfd, pt->pcdeflat->xoutbuf,
                4096ul, &pt->pcdeflat->xoutlen);
                           }
                           if (pt->pcdeflat->xoutlen==0L) {
                              pt->pcdeflat->done = 0;
                           }
                        }
                     }
                     else {
                        crlfp(pt->outbuf, 256ul, &pt->outlen, convtopipe);
                        wrpipe(pt->outfd, pt->outbuf, 256ul, &pt->outlen);
                     }
                  }
                  if (pt->outlen==0L) {
                     pt->outlen = (int32_t)l2_GetStr(pt->link0, 256U,
                (l2_pSTRING)pt->outbuf);
                     if (pt->outlen<=0L) break;
                  }
                  else break;
               }
            }
         }
         if (pt->junkbuf.len>0L) {
            if (pt->junkbuf.time0>0L) --pt->junkbuf.time0;
            else sendjunk(pt->link0, &pt->junkbuf);
         }
         pt = pt->next;
      }
   }
   X2C_EXIT();
   return 0;
}

X2C_MAIN_DEFINITION
