/* 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 STRINGINBUF si;
static CHAR buf[255];

#define CHECK_ADDR(v) do { \
  if (((ULONG)&v & ~0xffff) != (((ULONG)&v+sizeof(v)-1) & ~0xffff)) \
    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;

  si.cchIn = 0;
  for (;;)
    {
      si.cb = sizeof (buf);
      rc = KbdStringIn (buf, &si, IO_WAIT, 0);
      if (rc != 0)
        {
          fprintf (stderr, "\nKbdStringIn failed, rc=%u\n", (unsigned)rc);
          exit (1);
        }
      buf[si.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 (si);
  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);
}
