/* kbd.c (emx+gcc) */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INCL_KBD
#include <os2.h>


/* The header files of "IBM Developer's Toolkit for OS/2 2.0" lack
   the definition of KBDTRF_EXTENDED_CODE! */

#if !defined (KBDTRF_EXTENDED_CODE)
#define KBDTRF_EXTENDED_CODE 0x02
#endif

/* These variables must not cross a 64KB boundary.  Making them auto
   variables is dangerous. */

static KBDKEYINFO ki;
static CHAR buf[255];

#define CHECK_ADDR(ptr) do { \
    if (!_THUNK_PTR_STRUCT_OK (ptr)) abort(); } while (0)

static void test_charin (void)
{
  USHORT rc;

  for (;;)
    {
      rc = KbdCharIn (&ki, IO_WAIT, 0);
      if (rc != 0)
        {
          fprintf (stderr, "\nKbdCharIn failed, rc=%u\n", (unsigned)rc);
          exit (1);
        }
      if ((ki.chChar == 0 || ki.chChar == 0xe0)
          && ki.fbStatus & KBDTRF_EXTENDED_CODE)
        fputs ("<ext>", stdout);
      else if (ki.chChar == 0x1b)
        break;
      else
        putchar (ki.chChar);
    }
}


static void test_stringin (void)
{
  USHORT rc;
  STRINGINBUF si1, si2, *psi;

  /* At most one of si1, si2 crosses a 64Kbyte boundary.  Use one
     which doesn't. */

  psi = _THUNK_PTR_STRUCT_OK (&si1) ? &si1 : &si2;

  psi->cchIn = 0;
  for (;;)
    {
      psi->cb = sizeof (buf);
      rc = KbdStringIn (buf, psi, IO_WAIT, 0);
      if (rc != 0)
        {
          fprintf (stderr, "\nKbdStringIn failed, rc=%u\n", (unsigned)rc);
          exit (1);
        }
      buf[psi->cchIn] = 0;
      putchar ('\n');
      if (buf[0] == 0x1a)
        break;
      puts (buf);
    }
}


/* Move a structure to an address where it crosses a 64KB boundary.
   If the structure is not properly aligned, an exception occurs.  If
   the structure is properly aligned, the 16:16 pointer wraps around
   to an address 64KB lower than expected. */

static void test_bad_example (int trap)
{
  void *buf;
  ULONG addr;
  PKBDINFO pi;
  int i;

  buf = malloc (0x30000);
  if (buf == NULL)
    {
      fputs ("malloc() failed\n", stderr);
      exit (2);
    }
  memset (buf, 0, 0x30000);
  addr = (ULONG)buf + 0x10000;
  addr = addr | (trap ? 0xffff : 0xfffe);
  pi = (PKBDINFO)addr;
  pi->cb = sizeof (*pi);
  /* CHECK_ADDR (pi); */
  if (trap)
    {
      puts ("Exception expected..."); fflush (stdout);
    }
  KbdGetStatus (pi, 0);
  for (i = 0; i < 0x10000; ++i)
    if (((char *)(addr - 0x10000))[i] != 0)
      {
        puts ("Wrap around occured");
        return;
      }
}


static void usage (void)
{
  puts ("Usage: kbd [-stw]");
  exit (1);
}



int main (int argc, char *argv[])
{
  CHECK_ADDR (&ki);
  CHECK_ADDR (&buf);
  if (argc == 1)
    test_charin ();
  else if (argc != 2)
    usage ();
  else if (strcmp (argv[1], "-t") == 0)
    test_bad_example (TRUE);
  else if (strcmp (argv[1], "-w") == 0)
    test_bad_example (FALSE);
  else if (strcmp (argv[1], "-s") == 0)
    test_stringin ();
  else
    usage ();
  return 0;
}
