/*
 *  AMIGA.C - 19th Aug 1988 - System dependent functions.
 *   Pete Hardie VE5VA
 *
 */

#include "mb.h"
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <graphics/text.h>
#include <devices/serial.h>
#include <ctype.h>
#include <time.h>
#include "rexxhostbase.h"


extern short fbbrcv_flag; /* from rcvfbb() to getdat() - actually to charf() */
extern struct RexxHost      *rexx_host;
extern long rexxbit;
extern struct Window *mywindow;
extern struct IntuiMessage *NewMessage;
/* From mbfwd.c for the export/import functions */
extern short export,import;
extern FILE *eximfile;
short interflag = 0;
short debug;
long serfirst = 0L;
/* This has to be big enough to handle all of tmp->scr which is 16kb long */
char tmpstr[16*1024];
extern struct IOExtSer *Ser_Read;
extern struct IOStdReq *Con_Read,*Con_Write;
extern unsigned char rs_in[2];
extern struct IOExtSer *Ser_Write;
extern unsigned char rs_out[2];
extern char optflags[];
unsigned char gotstream = 0;
extern char *tcmds[];
/*
{
  "cono on\n",
  "cono of\n",
  "m on\n",
  "m of\n"
};
*/
char passchr = 0,hfstream = 0,vhfstream = 0;
char l_date[7], l_time[5];
char z_date[7], z_time[5];
char pt_flag = 0;
extern short tapr_flag;
short done_flag = 1;
/*
 *  Set the current I/O device.
 */

ioport(pp)
PORTS *pp;
{
  port = pp;
}

/*
 *  Return non-zero if a character waits at current input.
 */

instat()
{
  register int st;

   if (port->dev is p_console) {
      /* check for char at console */
      if(CheckIO((struct IORequest *)Con_Read))return(1);
      else return(0);
   }
   if(port->dev is p_tnc) {
      /* check for char at serial port */
      if(CheckIO((struct IORequest *)Ser_Read)) {
         if(optflags[4])interflag = 1;
         return(1);
      }
      else return(0);
   }
   return(0);
}
/* output char to serial port */
outi(ch)
char ch;
{
   schar(ch);
}

/*
 *  Put the character out the current port,
 *  echo to console if the current port is not the console.
 *  Note special handling of end-of-line character.
 */

outchar(ch)
char ch;
{
   if (ch is '\r') return;
   if (ch is '\n') ch = '\r';
/*
 *  Put it out to the console, if it is going there.
 */

   if ((port->dev is p_console) or port->ec or export)   {
      /* output '\n' */;
      if (ch is '\r') {
         if(export) {
            fputc('\n',eximfile);
            return;
         }
         else ttputc('\n');
      }
      else 
         if(ch != passchr)ttputc(ch);
   }

/*
 *  If the port is the console, nothing more to do.
 */

   if (port->dev is p_console) return;

/*
 *  Stuff this byte out the port.
 */

   outi(ch);
   if (ch is '\r')
      if (port->flags & p_lf) outi('\n');
}
unsigned char procstr[linelen];
/* This routine must not use tmpstr to process the string because tmpstr
   might be the input
*/
outstr(cp)
unsigned char *cp;
{
   register unsigned char *p,*q;
   register int beepcount;
   if(import)return;
   /* count the number of beeps in the line so that we can give an audible
      as well as visual beep
   */
   if((port->dev is p_console) || port->ec || export) {
      beepcount = 0;
      for(q = cp,p = &procstr[0];*q && p < &procstr[linelen-1];q++) {
         if(*q is '\r')continue;
         *p++ = *q;
         if(*q == '\007')beepcount++;
      }
      *p = 0;
      if(export) {
         fputs((char *)&procstr[0],eximfile);
         return;
      }
      Con_Write->io_Data = (APTR) &procstr[0];
      Con_Write->io_Length = p-&procstr[0];
      Con_Write->io_Command = CMD_WRITE;
      DoIO((struct IORequest *)Con_Write);
      initbeep();
      while(beepcount--) {
         beep();
         Delay(15L);
      }
      donebeep();
   }
   /* now do the TNC if it is required.
   */
   if(port->dev is p_console)return;
   for(q = cp,p = &procstr[0];*q && p < &procstr[linelen-1];q++) {
      if(*q is '\r')continue;
      if(*q is '\n') {
         *p++ = '\r';
         if (port->flags & p_lf)*p++ = '\n';
      }
      else *p++ = *q;
   }
   *p = 0;
   Ser_Write->IOSer.io_Command = CMD_WRITE;
   Ser_Write->IOSer.io_Length = p-&procstr[0];
   Ser_Write->IOSer.io_Data = (APTR) &procstr[0];
   DoIO((struct IORequest *)Ser_Write);
}

short wait_timeout = 0;
char cmdstring[100];
FILE *fd;
PORTS *devtnc;
char *inittnc(infile,force)
int force;
char *infile;
{
/* This routine MUST NOT use tmpstr while the infile string is being used.
   infile could be the tmpstr!
*/
   register char *cp;
   register char c;
   register PORTS *p;
   PORTS *oldport;
   char fileonly;


   fileonly = 0;
   oldport = port;
   wait_timeout = 0;
   devtnc = 0;
   /* Find the tnc port and set it as the current port */
   for(p=porthd; p != NULL; p = p->next) {
      if((devtnc == 0) && (p->id != 'Z'))devtnc = p;
   }
   if(devtnc == 0) {
      printf("Can't find port\n");
      endtimer();
      cleanser();
      freefcb();
      closeterm();
      clrbusy();
      exit(1);
   }
   ioport(devtnc);
   if((devtnc->dev == p_serial) || (devtnc->dev == p_nulmdm)) {
      devtnc->ec = 1;
      fileonly = 1;
      goto file_only;
   }
   if(!force) {
      fileonly = 1;
      goto file_only;
   }
   /* Send a break to the port if it is transparent */
   if(port->flags & p_trans) breakport();
   /* Force a ^Q^C X  to the KAM or other TNC. If KAM is in mode other
      than packet this will force it back.
      If already in packet mode this will be interpreted as an XFLOW
      request ... which won't hurt anything
   */
   outstr("\021\003X\n\n");
   waitcmd(10);
   /* have to turn off monitoring and conok so that nothing interferes
      with the startup
   */
   alloff();
   outstr("echo off\n"); /* echo MUST be off too */
   waitcmd(10);
   /* If wait_timeout is too big then the TNC probably isn't there
      so let's get outta here
   */
   if(wait_timeout >= 3) {
      fclose(port->fl);
      endtimer();
      cleanser();
      freefcb();
      closeterm();
      clrbusy();
      printf("Too many timeouts from TNC - possible reasons are:\n");
      printf("1. TNC and AMIGA are not using same baudrate\n");
      printf("2. The TNC is not plugged into the AMIGA\n");
      printf("3. The TNC PARITY is not set to NONE (or SPACE)\n");
      printf("4. There's too much monitored information buffered\n");
      printf("   and it took too long to clear it - try again\n");
      exit(1);
   }
   /* get the thing into sync with what comes in from the TNC which
      may have stored up a lot of monitored stuff
      Do this by sending out a command and watch for the reply.
   */
   outstr("txd\n");
   do {
      if((c = toupper(rdchar())) != 'T') {
         ttputc(c);
         continue;
      }
      ttputc('T');
      if((c = toupper(rdchar())) != 'X') {
         ttputc(c);
         continue;
      }
      ttputc('X');
      if((c = toupper(rdchar())) != 'D') {
         ttputc(c);
         continue;
      }
      ttputc('D');
      break;
   }while(1);
   waitcmd(0);
file_only:
   if((fd = fopen(infile,"r")) == NULL) {
      sprintf(tmpstr,"Can't open %s\n",infile);
      ioport(oldport);
      return(tmpstr);
   }
   while(fgets(cmdstring,99,fd) != NULL) {
      /* Ignore blank lines and comments */
      if((cmdstring[0] == 0) || (cmdstring[0] == 012)
                             || (cmdstring[0] == '#'))continue;
      if(cmdstring[0] == '!') {
         remnl(&cmdstring[0]);
         do_system(&cmdstring[1]);
         continue;
      }
      if(cmdstring[0] == '^') {
         /* If it is ^S, send it and don't bother to read any more
            of the file as the TNC won't respond anyway
         */
         if(toupper(cmdstring[1]) == 'S') {
            outchar('\023');
            break;
         }
         /* Ignore anything else. There's no point allowing ^Q because
            the startup code at the beginning of inittnc must force ^Q
            anyway, otherwise it wouldn't be able to get the tnc to
            respond.
         */
         continue;
      }
      /* Check for % command and, whether it is or not, copy the string
         to tmpstr
      */
      tnc_string(&cmdstring[0]);
      outstr(tmpstr);
      waitcmd(0);

   }
   if(fileonly) goto get_out;
   if(!passchr) {
      outstr("pass\n");
      while((c = toupper(rdchar())) != 'P') {
         ttputc(c);
         /* If the reply contains '?' then there's no pass command */
         if(c == '?') {
            ttputs("NO PASS COMMAND\n\n");
            goto get_out;
         }
      }
      ttputc(c);
      while((c = toupper(rdchar())) != '$')ttputc(c);
      ttputc(c);
      ttputc(c = rdchar());
      passchr = hex(c) << 4;
      ttputc(c = rdchar());
      passchr |= hex(c);
      waitcmd(0);
   }
   /* Now find out what the stream switching character(s) is(are) */
   /* but forget it if there is no pass char */
   if(passchr && !hfstream && !vhfstream) {    /* if not done before */
      outstr("streamsw\n");
      while((c = toupper(rdchar())) != 'W') {
         ttputc(c);
         if(c == '?') {
            /* something's gone wrong. If there is a passchar then there
               must also be a streamswitch. If not then clear passchr and
               announce the problem
            */
            passchr = 0;
            ttputs("Error from STREAMSW command - streamswitching off\n");
            waitcmd(0);
            goto get_out;
         }
      }
      ttputc(c);
      while((c = toupper(rdchar())) != '$')ttputc(c);
      ttputc(c);
      ttputc(c = rdchar());
      vhfstream = hex(c) << 4;
      ttputc(c = rdchar());
      vhfstream |= hex(c);
      /* IF it is a KAM there are two streamswitching chars to read */
      if((c = rdchar()) == '/') {
         ttputc(c);
         hfstream = vhfstream;
         ttputc(rdchar());         /* do the $ sign */
         ttputc(c = rdchar());
         vhfstream = hex(c) << 4;
         ttputc(c = rdchar());
         vhfstream |= hex(c);
      }
      else ttputc(c);
      waitcmd(0);
   }
   sprintf(cmdstring,"\nPASS = $%02x, streamswitch VHF = ",passchr);
   ttputs(cmdstring);
   if((vhfstream > ' ') && (vhfstream < 0177))
      sprintf(cmdstring,"%c",vhfstream);
   else
      sprintf(cmdstring,"$%02x",vhfstream);
   ttputs(cmdstring);
   if(hfstream) {
      if((hfstream > ' ') && (hfstream < 0177))
         sprintf(cmdstring," , HF = %c",hfstream);
      else
         sprintf(cmdstring," , HF = $%02x",hfstream);
      ttputs(cmdstring);
   }
   ttputc('\n');
get_out:
   fclose(fd);
   ioport(oldport);
   return((char *)0);
}
hex(c)
char c;
{
   char k;
   k = c;
   if((c >= '0') && (c <= '9'))return(c - '0');
   k = toupper(c);
   if((k >= 'A') && (c <= 'F'))return(c - 'A' + 10);
   return(-1);
}
flipdebug()
{
   debug ^= 1;
   if(debug) {
      ttputs("debug now ON\n");
   }
   else {
      ttputs("debug now OFF\n");
   }
}
extern unsigned char ichar;
extern long keybit,windbit,readbit,timerbit;
long getbits;
struct timeRequest {
   struct IORequest tr_node;
   struct timeval tr_time;
};
extern struct timeRequest timermsg;

/* replace the original do_idle because it busy-waited ...
   this version sleeps until there is something to do
*/
extern short cursecs;
do_idle()
{
   register unsigned char ch,*cp;
   register PORTS *p;
   int curmin,curmon;
   ULONG class,error;
   UWORD code;
   struct RexxMsg *rexxmessage;  /* incoming rexx messages */
   STRPTR Arg;       /* Temporary string pointer */
   PORTS *saveport;
   long waitbits;    /* waitbits MUST be local in CBBS because it is changed
                        by various other routines as they see fit */


   cursor_off();
   ioport(devtnc);
   allon();
   if((devtnc->dev == p_serial) || (devtnc->dev == p_nulmdm)) {
      ttputs("\nIDLE\n");
   }
   cp = (unsigned char *) devtnc->line;
   /* figure out time to next forward and set timer */
   curtim();
   curmin = 60 - cursecs;
   settimer(curmin);
   /* There might already be an unread char at the serial or console port */
   serfirst = 0L;
   clrbusy();
   if(CheckIO((struct IORequest *)Ser_Read)) {
      serfirst |= readbit;
      Wait(readbit);
   }
   if(CheckIO((struct IORequest *)Con_Read)) {
      serfirst |= keybit;
      Wait(keybit);
   }
   waitbits = windbit | readbit | timerbit | keybit | rexxbit;
   while(1)  {
      /* If there was already a tnc char waiting then do it now */
      if(serfirst) {
         getbits = serfirst;
         serfirst = 0L;
      }
      else {
         getbits = Wait(waitbits);
      }
      if(readbit & getbits) {
         /* save a line and if it is a CONNECT then return */
         if(error = WaitIO((struct IORequest *)Ser_Read)) {
            printf("DO_IDLE error %ld\n",error);
         }
         ch = rs_in[0];
         if(tapr_flag)ch &= 0177;
         *cp = ch;   /* autolf is off so don't need to look for '\n' */
         if((cp++ == (unsigned char *)devtnc->line + linelen -2)
                  || (ch == '\r')) {
            if(ch == '\r')cp--;
            *cp++ = '\n';
            *cp = 0;
            /* Str_search cleans up the line and then does outstr */
            ioport(cport);
            str_search(devtnc->line);
            ioport(devtnc);
            p->flags setbit p_give;
            if(iscon(devtnc->line)) {
               /* readbit has been handled but timerbit could be set */
               if(timerbit & getbits) {
                  WaitIO((struct IORequest *)&timermsg);
               }
               else {
                  cantimer();
               }
               BeginIO((struct IORequest *)Ser_Read);
               return;
            }
            monitor();
            cp = (unsigned char *)devtnc->line;
         }
         else if(ch == '\n')cp--;
         BeginIO((struct IORequest *)Ser_Read);
      }
      /* REXX must turn off the clock because the forward may do it
         anyway. Then once the forward is over, start the clock up
         again.
      */
      if(rexxbit & getbits) {
         while(rexxmessage = GetRexxMsg(rexx_host,0L)) {
            /* If Arg is non-zero then this is an AREXX message from another
               process.
               If Arg is null then it is the result of a ReplyRexxCommand
               from another process. This can't happen yet because we do not
               send messages out.
            */
            if(Arg = GetRexxCommand(rexxmessage)) {
               /* Must turn off the clock because the forward may do it
                  anyway. Then once the forward is over, start the clock up
                  again.
               */
               if(timerbit & getbits) {
                  WaitIO((struct IORequest *)&timermsg);
               }
               else {
                  cantimer();
               }
               getbits &= ~timerbit;
               saveport = port;
               /* Have to force it to use the console port for this */
               ioport(cport);
               port->opt1 = *Arg++;
               port->opt2 = *Arg ? *Arg: ' ';
               /* Have to reply to it early so that the sysop, or other
                  process doesn't have to wait for the forward to finish.
                  Send a non-zero secondary result to indicate OK.
               */
               ReplyRexxCommand(rexxmessage,0L,1L,0L);
               alloff();
               acmd();
               allon();
               ioport(saveport);
               /* Turn clock back on again */
               /* figure out time to next forward and set timer */
               curtim();
               curmin = 60 - cursecs;
               settimer(curmin);
            }
            else {
               FreeRexxCommand(rexxmessage);
            }
         }
      }
      if(getbits & keybit) {
         WaitIO((struct IORequest *)Con_Read);
         ch = ichar;
         Con_Read->io_Data = (APTR)&ichar;
         Con_Read->io_Command = CMD_READ;
         Con_Read->io_Length = 1;
         SendIO((struct IORequest *)Con_Read);
         if(ch == wchar) {
            ioport(cport);
            cursor_on();
            /* could have a timerbit set here.
               readbit could also be set but must handle that a
               different way.
            */
            if(timerbit & getbits) {
               WaitIO((struct IORequest *)&timermsg);
            }
            else {
               cantimer();
            }
            return;
         }
         if(ch == '\033') {
            ttputs("do_idle\n");
         }
      }
      if(windbit & getbits) {
         while(NewMessage = (struct IntuiMessage *)GetMsg(mywindow->UserPort)) {
            class = NewMessage->Class;
            code = NewMessage->Code;
            ReplyMsg((struct Message *)NewMessage);
            if(class == IDCMP_CLOSEWINDOW) {
               /* say a fond farewell, don't ask fer sure? */
               if(done_flag) {
                  if(timerbit & getbits) {
                     WaitIO((struct IORequest *)&timermsg);
                  }
                  else {
                     cantimer();
                  }
                  SetWindowTitles(mywindow,(UBYTE *)"",(UBYTE *)-1L);
                  /* Need this to cancel out the clrbusy in done() */
                  setbusy();
                  done(1);
               }
            }
         }
      }
      /* This code is deliberately placed last. If a timer bit occurs
         at the same time as a character arrives from the serial port
         then this code will hang the serial port up because afwd()
         tries to use the port again without clearing the existing char.
         So workaround it. Putting the timer code here guarantees that
         even if the ser char did occur it will be handled first and the
         ser char code handles the problem of returning from this routine
         with a timer bit set.
      */
      if(timerbit & getbits) {
         WaitIO((struct IORequest *)&timermsg);
         /* call the forwarding routine */
         curtim();
         curmin = 10 * (l_time[2] - '0') + (l_time[3] - '0');
         afwd(curmin);
         curmon = 10 * (l_date[2] - '0') + (l_date[3] - '0');

         if (s_param & s_log_on) if (curmon isnt log_mon) chglog();

         settimer(60);
      }
   }
}
/* IMPLEMENTATION BUG. In the original getdat it busy-idles and
   if the port is transparent and DCD goes off while the port is
   not idle, then it forces the mode to discon and then returns(true).
   This code can only do this properly IF it can wait for DCD to go
   off. I haven't even tried to implement this yet and not sure if it
   can be done properly. I assume that there's a way to Wait() for a
   DCD event but haven't checked it out
*/
char *cp;
/* MAXLEN is the longest unterminated line the justification routine
   will accept before it rips out the word and keeps it for later use.
   MAXJUST is the longest word that the routine will keep ... if the word
   is any longer than this then it will just allow you to mess up the line!
*/
#define MAXLEN 77
#define MAXJUST 20
unsigned char x_pos = 0; /* Position on line for right justification */
unsigned char s_pos = 0; /* Position of last space on the line */
char saveword[MAXJUST+2];
int cd_status = 0;
getdat()
{
   register char *q;
   register char ch;
   register PORTS *p;
   int eflag;
   int curmin,serflg;
   ULONG class,waitbits;
   USHORT code;
   int cd_temp;

   p = port;
   if(import) {
      if(fgets(p->line,250,eximfile) == NULL)p->line == 0;
      return(1);
   }
   cp = p->line;
   x_pos = s_pos = 0;
   /* If there was a justified word left over from previous line .. put it
      in here now.
   */
   if(*saveword) {
      q = saveword;
      while(*q) {
         ttputc(*q);
         *cp++ = *q++;
         x_pos++;
      }
   }
   *saveword = 0;
   *cp = 0;
   /* Is a timeout timer required? */
   curmin = 0;
   if(p->flags & p_dotmr) {
      if((p->mode & idle) && (p->dev == p_tnc))
         curmin = 2;
      else
         curmin = p->ctime;
   }
   /* The timer counts curmin down and each time it ticks the program
      queries the serial driver to see if CD is still on if the user
      is logged in on the modem. It doesn't bother for the sysop because
      the modem won't be active.
      I don't think that p_nulmdm needs to worry about this timer.
   */
/* NO YOU NITWIT! It won't check CD every second if the timer is set to
   300 seconds!
*/
   if(curmin) {
/*
      if(p->dev != p_serial) {
         settimer(curmin);
      }
      else {
*/
         settimer(1);   /* set timeout timer if required to look for CD */
/*
      }
*/
   }
   p->flags setbit p_dotmr;

   serfirst = 0L;
   if(CheckIO((struct IORequest *)Ser_Read)) {
      serfirst |= readbit;
      Wait(readbit);
   }
   if(p == cport) {
      waitbits = windbit | timerbit | keybit;
   }
   else {
      if(CheckIO((struct IORequest *)Con_Read)) {
         serfirst |= keybit;
         Wait(keybit);
      }
      waitbits = windbit | readbit | timerbit | keybit;
   }
   while(1)  {
      if(serfirst) {
         getbits = serfirst;
         serfirst = 0L;
      }
      else {
         getbits = Wait(waitbits);
      }
      if(readbit & getbits) {
         WaitIO((struct IORequest *)Ser_Read);
         ch = rs_in[0];
         if(!(p->flags & p_trans) & tapr_flag)ch &= 0177;
         BeginIO((struct IORequest *)Ser_Read);
         if(optflags[4])interflag = 1;
         /* ignore stuff from the port if we are reading the console */
         if((p != cport) && charf(ch)) {
            if(timerbit & getbits) {
               WaitIO((struct IORequest *)&timermsg);
            }
            else {
               if(curmin)cantimer();
            }
            if(!(p->flags & p_trans)) {
               if(isdis(p->line)) {
                  if(!(p->mode & idle))p->mode = discon;
                  return (true);
               }
               if(isretry(p->line)) {
                  return (false);
               }
               if(isreq(p->line)) {
                  p->flags setbit p_req;
                  pcall(p->rcall,p->line+20);
                  p->flags clrbit p_dotmr;
                  return (false);
               }
            }
            return (true);
         }
      }
      if(getbits & keybit) {
         WaitIO((struct IORequest *)Con_Read);
         ch = ichar;
         Con_Read->io_Data = (APTR)&ichar;
         Con_Read->io_Command = CMD_READ;
         Con_Read->io_Length = 1;
         SendIO((struct IORequest *)Con_Read);
         if(ch == '\033') {
            if(p == cport)ttputs("getdatCON\n");
            else          ttputs("getdatTNC\n");
            cd_temp = getserstat();
            sprintf(tmpstr,"serstat = %x\n",cd_temp);
            ttputs(tmpstr);
         }
         eflag = 0;
         if(p != cport) {
            if(ch == achar) {
               p->mode = forced;
               eflag = 1;
            }
            if(ch == tchar) {
               p->flags setbit p_opreq;
               eflag = 1;
            }
         }
         else {
            if(charf(ch)) {
               eflag = 1;
            }
         }
         /* If any of the previous three if statements want to
            exit then check that the timer is off and then get out
         */
         if(eflag) {
            if(timerbit & getbits) {
               WaitIO((struct IORequest *)&timermsg);
            }
            else {
               if(curmin)cantimer();
            }
            *cp = 0;
            return(true);
         }
      }
      if(windbit & getbits) {
         while(NewMessage = (struct IntuiMessage *)GetMsg(mywindow->UserPort)) {
            class = NewMessage->Class;
            code = NewMessage->Code;
            ReplyMsg((struct Message *)NewMessage);
            if(class == IDCMP_CLOSEWINDOW) {
               /* say a fond farewell, don't ask fer sure? */
               if(done_flag) {
                  if(timerbit & getbits) {
                     WaitIO((struct IORequest *)&timermsg);
                  }
                  else {
                     cantimer();
                  }
                  SetWindowTitles(mywindow,(UBYTE *)"",(UBYTE *)-1L);
                  done(1);
               }
            }
         }
      }
      if(timerbit & getbits) {
         WaitIO((struct IORequest *)&timermsg);
cd_temp = getserstat() & 0x20;
if(cd_temp != cd_status) {
/*
   ttputs("CD CHANGE - ");
   if(cd_temp)ttputs("ON\n");
   else       ttputs("OFF\n");
*/
   cd_status = cd_temp;
}
         /* FIRST: Look for drop of carrier detect on the serial port
            or TNC  IF it is in transparent mode
         */
         if((p->dev == p_serial) ||
               ((p->flags & p_trans) && (p->dev == p_tnc)) ) {
          if(getserstat() & 0x20) {
/*
ttputs("CD OFF - EXIT\n");
*/
               p->mode = discon;
               *cp = 0;
               return(true);
            }
         }
         if((curmin -= 1) <= 0) {
            if(!(p->mode & idle))p->mode = timeout;
            *cp = 0;
            return (true);
         }
         settimer(1);
      }
   }
}
charf(s)
unsigned char s;
{
   register unsigned char ch;
   PORTS *p;
   register char *q,*r;
   register int i;


   p = port;
   ch = s;
   /* Ignore characters with high order bit set...they tie up the TNC */
   /* UNLESS reading an FBB proposal or in transparent mode */
   if(!(p->flags & p_trans) && !p->tmode && !fbbrcv_flag) {
      if(ch > 0177) {
         return(0);
      }
   }
   *cp = ch;

   if((cp >= p->line + linelen - 3) || (ch == '\r')) {
do_lf:
      if(ch == '\r')*cp = '\n';
      *++cp = 0;
      if(p->dev != p_console)ttputs(p->line);
      if(p->flags & p_echo)ttputc('\n');
      return(1);
   }
   /* If in transparent mode or reading a proposal then store everything */
   if((p->flags & p_trans) || p->tmode || fbbrcv_flag) {
      cp++;
      return(0);
   }
   if((ch >= ' ') && (ch <= 0176)) {
      /*
         Do justification for local user.
      */
      if(p->dev == p_console) {
         if(ch == ' ') {
            s_pos = x_pos;
            /* If the space is at exactly the point we would have justified
               the line then fake a CR anyway
            */
            if(x_pos == MAXLEN) {
               ch = '\r';
               *saveword = 0;
               goto do_lf;
            }
         }
         else {
            /* It's not a space ... should we do the justify?
            */
            if(x_pos == MAXLEN) {
               /* This word can't be longer than MAXJUST or we just leave
                  it alone and let them mess up the line
               */
               if((i = (MAXLEN - s_pos)) < MAXJUST) {
                  q = saveword;
                  r = &p->line[s_pos+1];
                  do {
                     *q++ = *r++;
                     ttputs("\b \b");
                  }while(--i);
                  *q = 0;
                  cp = &p->line[s_pos];
                  ch = '\r';
                  goto do_lf;
               }
            }
         }
      }
      x_pos++;
      if (p->flags & p_echo) ttputc(ch);
      cp++;
      return(0);
   }
   switch(ch) {
   default:
      break;
   case 26:    /* Control - Z */
      cp++;
      x_pos++;
      break;
   case '\t':
      if(p->flags & p_echo) ttputc(ch);
      s_pos = x_pos;
      x_pos++;
      cp++;
      break;
   case '\b':
      if(cp > p->line) {
         cp--;
         if(p->flags & p_echo) ttputs("\b \b");
         x_pos--;
         /* If they wipe out a space we've got to find the previous one */
         if(*cp == ' ') {
            r = cp-1;
            while(r > p->line) {
               if(*r == ' ') break;
               r--;
            }
            s_pos = r - p->line;
         }

      }
      break;

   case '\n':
      if(cp is p->line) {
         *cp = '\0';
         return(1);
      }
      break;
   }
   return(0);
}


/* Remove the busy-wait in term() */

term(s)
PORTS *s;
{
   register char ch,*cp;
   register PORTS *m;
   ULONG class,waitbits;
   USHORT code;
   byte ec;

   m = cport;
   m->flags clrbit p_give;
   ec = s->ec;
   s->ec = true;
   ioport(m);
   serfirst = 0L;
   if(CheckIO((struct IORequest *)Ser_Read)) {
      serfirst |= readbit;
      Wait(readbit);
   }
   waitbits = windbit | readbit | keybit;
   while(1)  {
      if(serfirst) {
         getbits = serfirst;
         serfirst = 0L;
      }
      else {
         getbits = Wait(waitbits);
      }
      if(readbit & getbits) {
         WaitIO((struct IORequest *)Ser_Read);
         ch = rs_in[0];
         if(tapr_flag)ch &= 0177;
         BeginIO((struct IORequest *)Ser_Read);
/*         if(streamswitch(ch))continue;   */
         if(ch == '\r')ch = '\n';
         if(m->fl isnt NULL) if(ch isnt cpmeof)putc(ch,m->fl);
         ttputc(ch);
      }
      if(getbits & keybit) {
         WaitIO((struct IORequest *)Con_Read);
         ch = ichar;
         Con_Read->io_Data = (APTR)&ichar;
         Con_Read->io_Command = CMD_READ;
         Con_Read->io_Length = 1;
         SendIO((struct IORequest *)Con_Read);
         if(ch == rchar) {
            m->flags setbit p_give;
            s->ec = ec;
            return;
         }
         if(ch == '\r')ch = '\n';
         ttputc(ch);
         if(ch == '\n') {
            schar('\r');
            if(s->flags & p_lf)schar('\n');
         }
         else schar(ch);
         if(m->fl isnt NULL) if(ch isnt cpmeof)putc(ch,m->fl);
      }
      if(windbit & getbits) {
         while(NewMessage = (struct IntuiMessage *)GetMsg(mywindow->UserPort)) {
            class = NewMessage->Class;
            code = NewMessage->Code;
            ReplyMsg((struct Message *)NewMessage);
            if(class == IDCMP_CLOSEWINDOW) {
               /* say a fond farewell, don't ask fer sure? */
               if(done_flag) {
                  SetWindowTitles(mywindow,(UBYTE *)"",(UBYTE *)-1L);
                  done(1);
               }
            }
         }
      }
   }
}

/* This handles the stream characters.
   BUT not any more ... KAM V3.0 has removed the streamdb command.
   The first streamswitch character is always printed but is not passed
   as data.
   The character immediately after the streamswitch is passed if it is the
   same streamswitch character, otherwise it is printed but also not passed
   as data.
streamswitch(ch)
unsigned char ch;
{
   if(gotstream) {
      if(ch == gotstream) {
         gotstream = 0;
      }
      else {
         gotstream = 0;
         ttputc(ch);
         return(1);
      }
   }
   else {
      if(ch && ((ch == vhfstream) || (ch == hfstream))) {
         gotstream = ch;
         ttputc(ch);
         return(1);
      }
   }
   return(0);
}
*/


/* Replace the pause routine so that it can be used with TNC as well
   as the console. If routine times out after 2 minutes then it will
   return 'Q'.
*/
pause()
{

   register char ch,*q;
   ULONG class,waitbits;
   USHORT code;

   if (port->user->state & u_pause) return ' ';

   q = &tmpstr[0];
   prtx(pausemsg);
   if(port->mode != local)outchar('\n');
   settimer(120);   /* set timeout timer  */
   waitbits = windbit | readbit | timerbit | keybit;
   serfirst = 0L;
   if(CheckIO((struct IORequest *)Ser_Read)) {
      serfirst |= readbit;
      Wait(readbit);
   }
   while(1)  {
      if(serfirst) {
         getbits = serfirst;
         serfirst = 0L;
      }
      else {
         getbits = Wait(waitbits);
      }
      if(readbit & getbits) {
         WaitIO((struct IORequest *)Ser_Read);
         ch = rs_in[0];
         if(tapr_flag)ch &= 0177;
         ch = toupper(ch);
         BeginIO((struct IORequest *)Ser_Read);
         *q++ = ch;
         if(ch == '\r') {
            if(timerbit & getbits) {
               WaitIO((struct IORequest *)&timermsg);
            }
            else {
               cantimer();
            }
            return(tmpstr[0]);
         }
      }
      if(getbits & keybit) {
         WaitIO((struct IORequest *)Con_Read);
         ch = toupper(ichar);
         Con_Read->io_Data = (APTR)&ichar;
         Con_Read->io_Command = CMD_READ;
         Con_Read->io_Length = 1;
         SendIO((struct IORequest *)Con_Read);
         if(timerbit & getbits) {
            WaitIO((struct IORequest *)&timermsg);
         }
         else {
            cantimer();
         }
         outchar('\n');
         return(ch);
      }
      if(windbit & getbits) {
         while(NewMessage = (struct IntuiMessage *)GetMsg(mywindow->UserPort)) {
            class = NewMessage->Class;
            code = NewMessage->Code;
            ReplyMsg((struct Message *)NewMessage);
            if(class == IDCMP_CLOSEWINDOW) {
               /* say a fond farewell, don't ask fer sure? */
               if(done_flag) {
                  if(timerbit & getbits) {
                     WaitIO((struct IORequest *)&timermsg);
                  }
                  else {
                     cantimer();
                  }
                  SetWindowTitles(mywindow,(UBYTE *)"",(UBYTE *)-1L);
                  done(1);
               }
            }
         }
      }
      /* check for timeout */
      if(timerbit & getbits) {
         WaitIO((struct IORequest *)&timermsg);
         return('Q');
      }
   }
}

/* Force modem to hangup */
force_modem()
{
/*   if(getserstat() & 0x20)return;*/
   Delay(150L);
   outstr("+++");
   waitcmd(5);
   Delay(10L);
   outstr("ATH0\n");
   waitcmd(5);
}
/* replace waitcmd from mbtnc to reduce busy waits */
char *waitfor = "cmd:";
short wait_read;
waitcmd(x)
int x;
{

   register char ch;
   register int i;
   ULONG class,waitbits;
   USHORT code;
   wait_read = 0;
   if(port->dev == p_nulmdm)return(1);
   if(port->dev == p_serial) {
      waitfor = "ok\n\n";
   }
   else
      if(port->dev != p_tnc)return(1);
   if(s_flag & s_dv) i = 5;
   else i = 2;
   if(x > 0) i = x;
   settimer(i);   /* set timeout timer  */
   i = 0;
   waitbits = windbit | readbit | timerbit | keybit;
   serfirst = 0L;
   if(CheckIO((struct IORequest *)Ser_Read)) {
      serfirst |= readbit;
      Wait(readbit);
   }
   while(1)  {
      if(serfirst) {
         getbits = serfirst;
         serfirst = 0L;
      }
      else {
         getbits = Wait(waitbits);
      }
      if(readbit & getbits) {
         WaitIO((struct IORequest *)Ser_Read);
         wait_read = 1;
         ch = rs_in[0];
         if(tapr_flag)ch &= 0177;
         BeginIO((struct IORequest *)Ser_Read);
/*         if(streamswitch(ch))continue; */
         if(ch == '\r') {
            ch = '\n';
         }
         ttputc(ch);
         if(tolower(ch) == waitfor[i]) {
            i++;
            if(i == strlen(waitfor)) {
               if(timerbit & getbits) {
                  WaitIO((struct IORequest *)&timermsg);
               }
               else {
                  cantimer();
               }
               return(1);
            }
         }
         else {
            i = 0;
         }
      }
      if(keybit & getbits) {
         WaitIO((struct IORequest *)Con_Read);
         ch = ichar;
         Con_Read->io_Data = (APTR)&ichar;
         Con_Read->io_Command = CMD_READ;
         Con_Read->io_Length = 1;
         SendIO((struct IORequest *)Con_Read);
         if(ch == '\033') { /* ESC key forces termination */
            ttputs("ESC\n");
            if(timerbit & getbits) {
               WaitIO((struct IORequest *)&timermsg);
            }
            else {
               cantimer();
            }
            return(0);
         }
      }
      if(windbit & getbits) {
         while(NewMessage = (struct IntuiMessage *)GetMsg(mywindow->UserPort)) {
            class = NewMessage->Class;
            code = NewMessage->Code;
            ReplyMsg((struct Message *)NewMessage);
            if(class == IDCMP_CLOSEWINDOW) {
               /* say a fond farewell, don't ask fer sure? */
               if(done_flag) {
                  if(timerbit & getbits) {
                     WaitIO((struct IORequest *)&timermsg);
                  }
                  else {
                     cantimer();
                  }
                  SetWindowTitles(mywindow,(UBYTE *)"",(UBYTE *)-1L);
                  done(1);
               }
            }
         }
      }
      /* check for timeout */
      if(timerbit & getbits) {
         WaitIO((struct IORequest *)&timermsg);
         wait_timeout++;
         return(0);
      }
   }
}
/* Check for a TNC command that needs translating. The string should
   already have a \n on the end. The routine does not add one.
   Always copy the string into tmpstr even if it doesn't need translation.
   Currently this will only handle %l and %z which
   create a command in tmpstr to set the TNC date and time.
   The zone will be %l = local  or %z = UTC (Zulu)
   The remainder of the input command string will contain the letters
   Y M D H M S  wherever appropriate and will be replaced by TWO digits
   (with leading zero if necessary). So to create a string like "1994"
   you would specify an input string of "19Y".
*/
tnc_string(pp)
char *pp;
{
   register char *p,*q;
   char strsec[3];
   int zone;

   p = pp;
   if(*p != '%') {
      strcpy(tmpstr,pp);
      return;
   }
   q = tmpstr;
   p++;
   zone = -1;
   switch(tolower(*p)) {
   case 'l':
      zone = 0;
      break;
   case 'z':
      zone = 1;
      break;
   }
   if(zone == -1) {
      strcpy(tmpstr,pp);
      return;
   }
   curtim();
   p++;
   while(*p == ' ')p++;
   /* Copy the TNC command first */
   while(*p && (*p != ' ') && (*p != '\n'))*q++ = *p++;
   while(*p) {
      switch(*p) {
      case 'Y':
         if(zone) {
            *q++ = z_date[0];
            *q++ = z_date[1];
         }
         else {
            *q++ = l_date[0];
            *q++ = l_date[1];
         }
         break;
      case 'M':
         if(zone) {
            *q++ = z_date[2];
            *q++ = z_date[3];
         }
         else {
            *q++ = l_date[2];
            *q++ = l_date[3];
         }
         break;
      case 'D':
         if(zone) {
            *q++ = z_date[4];
            *q++ = z_date[5];
         }
         else {
            *q++ = l_date[4];
            *q++ = l_date[5];
         }
         break;
      case 'H':
         if(zone) {
            *q++ = z_time[0];
            *q++ = z_time[1];
         }
         else {
            *q++ = l_time[0];
            *q++ = l_time[1];
         }
         break;
      case 'N':
         if(zone) {
            *q++ = z_time[2];
            *q++ = z_time[3];
         }
         else {
            *q++ = l_time[2];
            *q++ = l_time[3];
         }
         break;
      case 'S':
         sprintf(strsec,"%02d",cursecs);
         *q++ = strsec[0];
         *q++ = strsec[1];
         break;
      default:
         *q++ = *p;
      }
      p++;
   }
   *q++ = 0;
}
