/* termio.c (emx+gcc) */

/* Test termio ioctl() and signals */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#include <errno.h>
#include <signal.h>
#include <process.h>
#include <setjmp.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/termio.h>

#define FALSE 0
#define TRUE  1

static int jump_flag = FALSE;
static int nread_flag = FALSE;
static int hex_flag = FALSE;
static int select_flag = FALSE;

static jmp_buf jump;

static void handler (int sig)
{
  if (sig == SIGINT)
    {
      printf ("(SIGINT)");
      signal (sig, SIG_ACK);
    }
  else if (sig == SIGALRM)
    {
      printf ("(SIGALRM)");
      signal (sig, SIG_ACK);
    }
  else
    {
      printf ("Signal %d received. Process stopped.\n", sig);
      exit (1);
    }
  if (jump_flag)
    longjmp (jump, 1);
}


static void hexb (unsigned char c)
{
  static char const hexd[] = "0123456789ABCDEF";
  char buf[2];

  buf[0] = hexd[(c >> 4) & 0x0f];
  buf[1] = hexd[c & 0x0f];
  write (1, buf, 2);
}


static void usage (void)
{
  puts ("Usage: termio [-jnh] [-s[#]]");
  exit (1);
}


int main (int argc, char *argv[])
{
  char buf[100], *p;
  int i, n, req;
  struct termio tio;
  fd_set rfds;
  struct timeval tv, *tvp;

  for (i = 1; i < argc; ++i)
    if (strcmp (argv[i], "-j") == 0)
      jump_flag = TRUE;
    else if (strcmp (argv[i], "-n") == 0)
      nread_flag = TRUE;
    else if (strcmp (argv[i], "-h") == 0)
      hex_flag = TRUE;
    else if (memcmp (argv[i], "-s", 2) == 0)
      {
        select_flag = TRUE;
        if (argv[i][2] == 0)
          tvp = NULL;
        else
          {
            errno  = 0;
            tv.tv_sec = strtol (argv[i]+2, &p, 10);
            if (errno != 0 || *p != 0)
              usage ();
            tv.tv_usec = 0;
            tvp = &tv;
          }
        }
    else
      usage ();
  if (setjmp (jump) != 0)
    puts ("jumped");
  signal (SIGINT, handler);
  signal (SIGBREAK, handler);
  signal (SIGQUIT, handler);
  signal (SIGALRM, handler);
  ioctl (0, TCGETA, &tio);
  tio.c_lflag &= ~IDEFAULT;
  ioctl (0, TCSETA, &tio);
  req = sizeof (buf) - 1;
  for (;;)
    {
      if (nread_flag)
        {
          if (ioctl (0, FIONREAD, &n) == -1)
            perror ("ioctl FIONREAD");
          else
            printf ("%d> ", n);
        }
      if (select_flag)
        {
          FD_ZERO (&rfds);
          FD_SET (0, &rfds);
          i = select (FD_SETSIZE, &rfds, NULL, NULL, tvp);
          if (i < 0)
            perror ("select");
          else if (i == 0)
            printf ("0> ");
          else
            {
              printf ("%d:", i);
              for (i = 7; i >= 0; --i)
                printf ("%d", (FD_ISSET (i, &rfds) ? 1 : 0));
              printf ("> ");
            }
        }
      n = read (0, buf, req);
      if (n < 0)
        {
          if (errno == EAGAIN)
            {
              puts ("No data available -- sleeping for 2 seconds");
              sleep (2);
            }
          else
            perror ("read");
        }
      if (n >= 0)
        {
          write (1, "<", 1);
          if (hex_flag)
            for (i = 0; i < n; ++i)
              hexb (buf[i]);
          else
            write (1, buf, n);
          write (1, ">\n", 2);
        }
      if (n > 0)
        {
          buf[n] = 0;
          p = strchr (buf, '\n');
          if (p != NULL) *p = 0;
          if (buf[0] == '?')
            {
              puts ("?          Help");
              puts ("$          Quit");
              puts ("!CMD       Shell escape");
              puts ("+          Toggle ICANON");
              puts (":          Toggle IDEFAULT");
              puts ("|          Toggle IDELETE");
              puts (";          Toggle ECHO");
              puts ("-          Toggle O_NDELAY");
              puts ("%SEC       Set alarm clock");
              puts ("#SEC       Sleep (append 'f' to flush buffer)");
              puts ("=CHARS     Set number of characters to read");
              puts (">CHARS     Set VMIN");
              puts ("<TENTHS    Set VTIME");
              puts (".SIG       Ignore signal");
              puts ("*SIG       Handle signal");
              puts ("^SIG       Set default processing for signal");
              puts ("~SIG       Raise signal");
            }
          else if (buf[0] == '$')
            break;
          else if (buf[0] == '!')
            system (buf+1);
          else if (buf[0] == '%')
            printf ("alarm=%u\n", alarm (atoi (buf+1)));
          else if (buf[0] == '#')
            {
              n = atoi (buf+1);
              if (n > 0)
                sleep (n);
              if (strchr (buf+1, 'f'))
                ioctl (0, TCFLSH, 0);
            }
          else if (buf[0] == '+')
            {
              tio.c_lflag ^= ICANON;
              ioctl (0, TCSETA, &tio);
            }
          else if (buf[0] == ':')
            {
              tio.c_lflag ^= IDEFAULT;
              ioctl (0, TCSETA, &tio);
            }
          else if (buf[0] == ';')
            {
              tio.c_lflag ^= ECHO;
              ioctl (0, TCSETA, &tio);
            }
          else if (buf[0] == '|')
            {
              tio.c_iflag ^= IDELETE;
              ioctl (0, TCSETA, &tio);
            }
          else if (buf[0] == '>')
            {
              tio.c_cc[VMIN] = (unsigned char)atoi (buf+1);
              ioctl (0, TCSETA, &tio);
            }
          else if (buf[0] == '<')
            {
              tio.c_cc[VTIME] = (unsigned char)atoi (buf+1);
              ioctl (0, TCSETA, &tio);
            }
          else if (buf[0] == '.')
            signal (atoi (buf+1), SIG_IGN);
          else if (buf[0] == '*')
            signal (atoi (buf+1), handler);
          else if (buf[0] == '^')
            signal (atoi (buf+1), SIG_DFL);
          else if (buf[0] == '~')
            {
              if (buf[1] == 'a')
                abort ();
              else
                raise (atoi (buf+1));
            }
          else if (buf[0] == '=')
            {
              n = atoi (buf+1);
              if (n >= 1 && n <= sizeof (buf) - 1)
                req = n;
              else
                req = sizeof (buf) - 1;
            }
          else if (buf[0] == '-')
            {
              n = fcntl (0, F_GETFL, 0);
              if (n >= 0)
                {
                  n ^= O_NDELAY;
                  fcntl (0, F_SETFL, n);
                }
            }
        }
    }
  return (0);
}
