/* tngets.c */

/* Copyright 1997 by Eberhard Mattes <mattes@azu.informatik.uni-stuttgart.de>
   Donated to the public domain.  No warranty.

   1997-01-04 Initial version
   1997-01-17 Rename flags, add flag arg to tn_init()
   1997-01-18 Fix various bugs
   1997-04-05 Reorganize library, don't trap SIGALRM
   1997-04-09 Eat line feed in dumb mode
   1007-07-18 Don't backspace if echo is disabled */

#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <arpa/telnet.h>
#include "firewall.h"
#include "libemtn.h"
#include "tnint.h"


static void tn_gets_telnet (tnconn *t, char *buf, size_t buf_size,
                            unsigned flags)
{
  int x, c, cmd, opt;
  unsigned char cmdbuf[3];

  if (!t->suppress_go_ahead)
    {
      cmdbuf[0] = IAC; cmdbuf[1] = GA;
      tn_write (t, (char*) cmdbuf, 2);
    }
  x = 0;
  for (;;)
    {
      c = tn_getc (t);
      if (c == IAC)
	{
	  cmd = tn_getc (t);
	  if (cmd == DO || cmd == DONT || cmd == WILL || cmd == WONT)
	    {
	      opt = tn_getc (t);
	      /* We have to avoid a negotiation loop, see tn_init(). */
	      if (opt == TELOPT_SGA && cmd == DO)
		t->suppress_go_ahead = 1;
	      else if (opt == TELOPT_SGA && cmd == DONT)
		t->suppress_go_ahead = 0;
	      else if (opt != TELOPT_ECHO && opt != TELOPT_BINARY)
		{
		  cmdbuf[0] = IAC;
		  cmdbuf[1] = (cmd == DO || cmd == DONT) ? WONT : DONT;
		  cmdbuf[2] = opt;
		  tn_write (t, (char*) cmdbuf, 3);
		}
	      continue;
	    }
	  else if (cmd == EC)
	    c = 0x08;
	  else if (cmd == EL)
	    c = 0x15;
	  else if (cmd != IAC)
	    continue;
	}
      if (c == 0x08 || c == 0x7f)
	{
	  if (x == 0)
	    tn_putc (t, '\a');
	  else
	    {
	      --x;
              if (flags & TN_GETS_ECHO)
                tn_write (t, "\b \b", 3);
	    }
	}
      else if (c == 0x15)
	{
	  while (x > 0)
	    {
	      --x;
	      if (flags & TN_GETS_ECHO)
		tn_write (t, "\b \b", 3);
	    }
	}
      else if (c == 0x0a || c == 0x00)
	;
      else if (c == 0x0d)
	{
	  buf[x] = 0;
	  tn_putnl (t);
	  return;
	}
      else if (c < 0x20 || x + 1 >= buf_size)
	tn_putc (t, '\a');
      else
	{
	  buf[x++] = (char)c;
	  if (flags & TN_GETS_ECHO)
	    tn_putc (t, (char)c);
	}
    }
}


static void tn_gets_dumb (tnconn *t, char *buf, size_t buf_size)
{
  int x, c;

  x = 0;
  for (;;)
    {
      c = tn_getc (t);
      while (c == 0x0d)
	c = tn_getc (t);
      if (c == 0x0a)
	{
	  buf[x] = 0;
	  return;
        }
      else if (c >= 0x20 && x + 1 < buf_size)
        buf[x++] = (char)c;
    }
}


void tn_gets (tnconn *t, char *buf, size_t buf_size, int timeout,
	      unsigned flags)
{
  if (timeout > 0)
    {
      /* Terminate without logging to avoid reentrancy problems. */
      signal (SIGALRM, SIG_DFL);
      alarm (timeout);
    }
  if (t->init_flags & TN_INIT_TELNET)
    tn_gets_telnet (t, buf, buf_size, flags);
  else
    tn_gets_dumb (t, buf, buf_size);
  if (timeout > 0)
    alarm (0);
}
