/* fhandler_console.cc

   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
   2006, 2008, 2009, 2010, 2011 Red Hat, Inc.

This file is part of Cygwin.

This software is a copyrighted work licensed under the terms of the
Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
details. */

#include "winsup.h"
#include "miscfuncs.h"
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <wingdi.h>
#include <winuser.h>
#include <winnls.h>
#include <ctype.h>
#include <sys/cygwin.h>
#include <cygwin/kd.h>
#include "cygerrno.h"
#include "security.h"
#include "path.h"
#include "fhandler.h"
#include "dtable.h"
#include "cygheap.h"
#include "sigproc.h"
#include "pinfo.h"
#include "shared_info.h"
#include "cygtls.h"
#include "tls_pbuf.h"
#include "registry.h"

/* Don't make this bigger than NT_MAX_PATH as long as the temporary buffer
   is allocated using tmp_pathbuf!!! */
#define CONVERT_LIMIT NT_MAX_PATH

/*
 * Scroll the screen context.
 * x1, y1 - ul corner
 * x2, y2 - dr corner
 * xn, yn - new ul corner
 * Negative values represents current screen dimensions
 */

#define srTop (dev_state->info.winTop + dev_state->scroll_region.Top)
#define srBottom ((dev_state->scroll_region.Bottom < 0) ? dev_state->info.winBottom : dev_state->info.winTop + dev_state->scroll_region.Bottom)

#define use_tty ISSTATE (myself, PID_USETTY)

const char *get_nonascii_key (INPUT_RECORD&, char *);

const unsigned fhandler_console::MAX_WRITE_CHARS = 16384;

static console_state NO_COPY *shared_console_info;

dev_console NO_COPY *fhandler_console::dev_state;

static void
beep ()
{
  reg_key r (HKEY_CURRENT_USER, KEY_ALL_ACCESS, "AppEvents", "Schemes", "Apps",
	     ".Default", ".Default", ".Current", NULL);
  if (r.created ())
    {
      char *buf = NULL;
      UINT len = GetWindowsDirectory (buf, 0);
      buf = (char *) alloca (len += sizeof ("\\media\\ding.wav"));
      UINT res = GetWindowsDirectory (buf, len);
      if (res && res <= len)
	r.set_string ("", strcat (buf, "\\media\\ding.wav"));
    }
  MessageBeep (MB_OK);
}

/* Allocate and initialize the shared record for the current console.
   Returns a pointer to shared_console_info. */
tty_min *
fhandler_console::get_tty_stuff (int flags = 0)
{
  if (dev_state)
    return &shared_console_info->tty_min_state;

  shared_console_info =
    (console_state *) open_shared (NULL, 0, cygheap->console_h,
				   sizeof (*shared_console_info),
				   SH_SHARED_CONSOLE);
  dev_state = &shared_console_info->dev_state;

  ProtectHandleINH (cygheap->console_h);
  if (!shared_console_info->tty_min_state.ntty)
    {
      shared_console_info->tty_min_state.setntty (TTY_CONSOLE);
      shared_console_info->tty_min_state.setsid (myself->sid);
      myself->set_ctty (&shared_console_info->tty_min_state, flags, NULL);

      dev_state->scroll_region.Bottom = -1;
      dev_state->dwLastCursorPosition.X = -1;
      dev_state->dwLastCursorPosition.Y = -1;
      dev_state->dwLastMousePosition.X = -1;
      dev_state->dwLastMousePosition.Y = -1;
      dev_state->dwLastButtonState = 0;	/* none pressed */
      dev_state->last_button_code = 3;	/* released */
      dev_state->underline_color = FOREGROUND_GREEN | FOREGROUND_BLUE;
      dev_state->dim_color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
      dev_state->meta_mask = LEFT_ALT_PRESSED;
      /* Set the mask that determines if an input keystroke is modified by
	 META.  We set this based on the keyboard layout language loaded
	 for the current thread.  The left <ALT> key always generates
	 META, but the right <ALT> key only generates META if we are using
	 an English keyboard because many "international" keyboards
	 replace common shell symbols ('[', '{', etc.) with accented
	 language-specific characters (umlaut, accent grave, etc.).  On
	 these keyboards right <ALT> (called AltGr) is used to produce the
	 shell symbols and should not be interpreted as META. */
      if (PRIMARYLANGID (LOWORD (GetKeyboardLayout (0))) == LANG_ENGLISH)
	dev_state->meta_mask |= RIGHT_ALT_PRESSED;
      dev_state->set_default_attr ();
      dev_state->backspace_keycode = CERASE;
      shared_console_info->tty_min_state.sethwnd ((HWND) INVALID_HANDLE_VALUE);
    }

  return &shared_console_info->tty_min_state;
}

void
set_console_ctty ()
{
  fhandler_console::get_tty_stuff ();
}

/* Return the tty structure associated with a given tty number.  If the
   tty number is < 0, just return a dummy record. */
tty_min *
tty_list::get_tty (int n)
{
  static tty_min nada;
  if (n == TTY_CONSOLE)
    return fhandler_console::get_tty_stuff ();
  else if (n >= 0)
    return &cygwin_shared->tty.ttys[n];
  else
    return &nada;
}

inline DWORD
dev_console::con_to_str (char *d, int dlen, WCHAR w)
{
  return sys_cp_wcstombs (cygheap->locale.wctomb, cygheap->locale.charset,
			  d, dlen, &w, 1);
}

inline UINT
dev_console::get_console_cp ()
{
  /* The alternate charset is always 437, just as in the Linux console. */
  return alternate_charset_active ? 437 : 0;
}

inline DWORD
dev_console::str_to_con (mbtowc_p f_mbtowc, const char *charset,
			 PWCHAR d, const char *s, DWORD sz)
{
  return sys_cp_mbstowcs (f_mbtowc, charset, d, CONVERT_LIMIT, s, sz);
}

bool
fhandler_console::set_raw_win32_keyboard_mode (bool new_mode)
{
  bool old_mode = dev_state->raw_win32_keyboard_mode;
  dev_state->raw_win32_keyboard_mode = new_mode;
  syscall_printf ("raw keyboard mode %sabled", dev_state->raw_win32_keyboard_mode ? "en" : "dis");
  return old_mode;
};

void
fhandler_console::set_cursor_maybe ()
{
  CONSOLE_SCREEN_BUFFER_INFO now;

  if (!GetConsoleScreenBufferInfo (get_output_handle (), &now))
    return;

  if (dev_state->dwLastCursorPosition.X != now.dwCursorPosition.X ||
      dev_state->dwLastCursorPosition.Y != now.dwCursorPosition.Y)
    {
      SetConsoleCursorPosition (get_output_handle (), now.dwCursorPosition);
      dev_state->dwLastCursorPosition = now.dwCursorPosition;
    }
}

void
fhandler_console::send_winch_maybe ()
{
  SHORT y = dev_state->info.dwWinSize.Y;
  SHORT x = dev_state->info.dwWinSize.X;
  dev_state->fillin_info (get_output_handle ());

  if (y != dev_state->info.dwWinSize.Y || x != dev_state->info.dwWinSize.X)
    {
      extern fhandler_tty_master *tty_master;
      dev_state->scroll_region.Top = 0;
      dev_state->scroll_region.Bottom = -1;
      if (tty_master)
	tty_master->set_winsize (true);
      else
	tc->kill_pgrp (SIGWINCH);
    }
}

/* Check whether a mouse event is to be reported as an escape sequence */
bool
fhandler_console::mouse_aware (MOUSE_EVENT_RECORD& mouse_event)
{
  if (! dev_state->use_mouse)
    return 0;

  /* Adjust mouse position by window scroll buffer offset
     and remember adjusted position in state for use by read() */
  CONSOLE_SCREEN_BUFFER_INFO now;
  if (GetConsoleScreenBufferInfo (get_output_handle (), &now))
    {
      dev_state->dwMousePosition.X = mouse_event.dwMousePosition.X - now.srWindow.Left;
      dev_state->dwMousePosition.Y = mouse_event.dwMousePosition.Y - now.srWindow.Top;
    }
  else
    {
      /* Cannot adjust position by window scroll buffer offset */
      return 0;
    }

  /* Check whether adjusted mouse position can be reported */
  if (dev_state->dwMousePosition.X > 0xFF - ' ' - 1
      || dev_state->dwMousePosition.Y > 0xFF - ' ' - 1)
    {
      /* Mouse position out of reporting range */
      return 0;
    }

  return ((mouse_event.dwEventFlags == 0 || mouse_event.dwEventFlags == DOUBLE_CLICK)
	  && mouse_event.dwButtonState != dev_state->dwLastButtonState)
	 || mouse_event.dwEventFlags == MOUSE_WHEELED
	 || (mouse_event.dwEventFlags == MOUSE_MOVED
	     && (dev_state->dwMousePosition.X != dev_state->dwLastMousePosition.X
	         || dev_state->dwMousePosition.Y != dev_state->dwLastMousePosition.Y)
	     && ((dev_state->use_mouse >= 2 && mouse_event.dwButtonState)
	         || dev_state->use_mouse >= 3));
}

void __stdcall
fhandler_console::read (void *pv, size_t& buflen)
{
  HANDLE h = get_io_handle ();

#define buf ((char *) pv)

  int ch;
  set_input_state ();

  int copied_chars = get_readahead_into_buffer (buf, buflen);

  if (copied_chars)
    {
      buflen = copied_chars;
      return;
    }

  HANDLE w4[2];
  DWORD nwait;
  char tmp[60];

  w4[0] = h;
  if (&_my_tls != _main_tls)
    nwait = 1;
  else
    {
      w4[1] = signal_arrived;
      nwait = 2;
    }

  termios ti = tc->ti;
  for (;;)
    {
      int bgres;
      if ((bgres = bg_check (SIGTTIN)) <= bg_eof)
	{
	  buflen = bgres;
	  return;
	}

      set_cursor_maybe ();	/* to make cursor appear on the screen immediately */
      switch (WaitForMultipleObjects (nwait, w4, FALSE, INFINITE))
	{
	case WAIT_OBJECT_0:
	  break;
	case WAIT_OBJECT_0 + 1:
	  goto sig_exit;
	default:
	  goto err;
	}

      DWORD nread;
      INPUT_RECORD input_rec;
      const char *toadd = NULL;

      if (!ReadConsoleInputW (h, &input_rec, 1, &nread))
	{
	  syscall_printf ("ReadConsoleInput failed, %E");
	  goto err;		/* seems to be failure */
	}

      /* check the event that occurred */
      switch (input_rec.EventType)
	{
	case KEY_EVENT:
#define virtual_key_code (input_rec.Event.KeyEvent.wVirtualKeyCode)
#define control_key_state (input_rec.Event.KeyEvent.dwControlKeyState)

	  dev_state->nModifiers = 0;

#ifdef DEBUGGING
	  /* allow manual switching to/from raw mode via ctrl-alt-scrolllock */
	  if (input_rec.Event.KeyEvent.bKeyDown &&
	      virtual_key_code == VK_SCROLL &&
	      ((control_key_state & (LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED)) == (LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED))
	    )
	    {
	      set_raw_win32_keyboard_mode (!dev_state->raw_win32_keyboard_mode);
	      continue;
	    }
#endif

	  if (dev_state->raw_win32_keyboard_mode)
	    {
	      __small_sprintf (tmp, "\033{%u;%u;%u;%u;%u;%luK",
				    input_rec.Event.KeyEvent.bKeyDown,
				    input_rec.Event.KeyEvent.wRepeatCount,
				    input_rec.Event.KeyEvent.wVirtualKeyCode,
				    input_rec.Event.KeyEvent.wVirtualScanCode,
				    input_rec.Event.KeyEvent.uChar.UnicodeChar,
				    input_rec.Event.KeyEvent.dwControlKeyState);
	      toadd = tmp;
	      nread = strlen (toadd);
	      break;
	    }

#define ich (input_rec.Event.KeyEvent.uChar.AsciiChar)
#define wch (input_rec.Event.KeyEvent.uChar.UnicodeChar)
#define ALT_PRESSED (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)
#define CTRL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)

	  /* Ignore key up events, except for left alt events with non-zero character
	   */
	  if (!input_rec.Event.KeyEvent.bKeyDown &&
	      /*
		Event for left alt, with a non-zero character, comes from
		"alt + numerics" key sequence.
		e.g. <left-alt> 0233 => &eacute;
	      */
	      !(wch != 0
		// ?? experimentally determined on an XP system
		&& virtual_key_code == VK_MENU
		// left alt -- see http://www.microsoft.com/hwdev/tech/input/Scancode.asp
		&& input_rec.Event.KeyEvent.wVirtualScanCode == 0x38))
	    continue;

	  if (control_key_state & SHIFT_PRESSED)
	    dev_state->nModifiers |= 1;
	  if (control_key_state & RIGHT_ALT_PRESSED)
	    dev_state->nModifiers |= 2;
	  if (control_key_state & CTRL_PRESSED)
	    dev_state->nModifiers |= 4;
	  if (control_key_state & LEFT_ALT_PRESSED)
	    dev_state->nModifiers |= 8;

	  if (input_rec.Event.KeyEvent.wVirtualScanCode == 14)
	    {
	      char c = dev_state->backspace_keycode;
	      nread = 0;
	      if (control_key_state & ALT_PRESSED) {
		if (dev_state->metabit)
		  c |= 0x80;
		else
		  tmp[nread++] = '\e';
	      }
	      tmp[nread++] = c;
	      tmp[nread] = 0;
	      toadd = tmp;
	    }
	  /* Allow Ctrl-Space to emit ^@ */
	  else if (input_rec.Event.KeyEvent.wVirtualKeyCode == VK_SPACE
		   && (control_key_state & CTRL_PRESSED))
	    toadd = "";
	  else if (wch == 0
	      /* arrow/function keys */
	      || (input_rec.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY))
	    {
	      toadd = get_nonascii_key (input_rec, tmp);
	      if (!toadd)
		{
		  dev_state->nModifiers = 0;
		  continue;
		}
	      nread = strlen (toadd);
	    }
	  else
	    {
	      nread = dev_state->con_to_str (tmp + 1, 59, wch);
	      /* Determine if the keystroke is modified by META.  The tricky
		 part is to distinguish whether the right Alt key should be
		 recognized as Alt, or as AltGr. */
	      bool meta =
		     /* Alt but not AltGr (= left ctrl + right alt)? */
		     (control_key_state & ALT_PRESSED) != 0
		     && ((control_key_state & CTRL_PRESSED) == 0
			    /* but also allow Alt-AltGr: */
			 || (control_key_state & ALT_PRESSED) == ALT_PRESSED
			 || (wch <= 0x1f || wch == 0x7f));
	      if (!meta)
		{
		  /* Determine if the character is in the current multibyte
		     charset.  The test is easy.  If the multibyte sequence
		     is > 1 and the first byte is ASCII CAN, the character
		     has been translated into the ASCII CAN + UTF-8 replacement
		     sequence.  If so, just ignore the keypress.
		     FIXME: Is there a better solution? */
		  if (nread > 1 && tmp[1] == 0x18)
		    beep ();
		  else
		    toadd = tmp + 1;
		}
	      else if (dev_state->metabit)
		{
		  tmp[1] |= 0x80;
		  toadd = tmp + 1;
		}
	      else
		{
		  tmp[0] = '\033';
		  tmp[1] = cyg_tolower (tmp[1]);
		  toadd = tmp;
		  nread++;
		  dev_state->nModifiers &= ~4;
		}
	    }
#undef ich
#undef wch
#undef ALT_PRESSED
#undef CTRL_PRESSED
	  break;

	case MOUSE_EVENT:
	  send_winch_maybe ();
	  {
	    MOUSE_EVENT_RECORD& mouse_event = input_rec.Event.MouseEvent;
	    /* As a unique guard for mouse report generation, 
	       call mouse_aware() which is common with select(), so the result 
	       of select() and the actual read() will be consistent on the 
	       issue of whether input (i.e. a mouse escape sequence) will 
	       be available or not */
	    if (mouse_aware (mouse_event))
	      {
		/* Note: Reported mouse position was already retrieved by 
		   mouse_aware() and adjusted by window scroll buffer offset */

		/* Treat the double-click event like a regular button press */
		if (mouse_event.dwEventFlags == DOUBLE_CLICK)
		  {
		    syscall_printf ("mouse: double-click -> click");
		    mouse_event.dwEventFlags = 0;
		  }

		/* This code assumes Windows never reports multiple button
		   events at the same time. */
		int b = 0;
		char sz[32];

		if (mouse_event.dwEventFlags == MOUSE_WHEELED)
		  {
		    if (mouse_event.dwButtonState & 0xFF800000)
		      {
			b = 0x41;
			strcpy (sz, "wheel down");
		      }
		    else
		      {
			b = 0x40;
			strcpy (sz, "wheel up");
		      }
		  }
		else
		  {
		    /* Ignore unimportant mouse buttons */
		    mouse_event.dwButtonState &= 0x7;

		    if (mouse_event.dwEventFlags == MOUSE_MOVED)
		      {
			b = dev_state->last_button_code;
		      }
		    else if (mouse_event.dwButtonState < dev_state->dwLastButtonState)
		      {
			b = 3;
			strcpy (sz, "btn up");
		      }
		    else if ((mouse_event.dwButtonState & 1) != (dev_state->dwLastButtonState & 1))
		      {
			b = 0;
			strcpy (sz, "btn1 down");
		      }
		    else if ((mouse_event.dwButtonState & 2) != (dev_state->dwLastButtonState & 2))
		      {
			b = 2;
			strcpy (sz, "btn2 down");
		      }
		    else if ((mouse_event.dwButtonState & 4) != (dev_state->dwLastButtonState & 4))
		      {
			b = 1;
			strcpy (sz, "btn3 down");
		      }

		    dev_state->last_button_code = b;

		    if (mouse_event.dwEventFlags == MOUSE_MOVED)
		      {
			b += 32;
			strcpy (sz, "move");
		      }
		    else
		      {
			/* Remember the modified button state */
			dev_state->dwLastButtonState = mouse_event.dwButtonState;
		      }
		  }

		/* Remember mouse position */
		dev_state->dwLastMousePosition.X = dev_state->dwMousePosition.X;
		dev_state->dwLastMousePosition.Y = dev_state->dwMousePosition.Y;

		/* Remember the modifiers */
		dev_state->nModifiers = 0;
		if (mouse_event.dwControlKeyState & SHIFT_PRESSED)
		    dev_state->nModifiers |= 0x4;
		if (mouse_event.dwControlKeyState & (RIGHT_ALT_PRESSED|LEFT_ALT_PRESSED))
		    dev_state->nModifiers |= 0x8;
		if (mouse_event.dwControlKeyState & (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED))
		    dev_state->nModifiers |= 0x10;

		/* Indicate the modifiers */
		b |= dev_state->nModifiers;

		/* We can now create the code. */
		sprintf (tmp, "\033[M%c%c%c", b + ' ', dev_state->dwMousePosition.X + ' ' + 1, dev_state->dwMousePosition.Y + ' ' + 1);
		syscall_printf ("mouse: %s at (%d,%d)", sz, dev_state->dwMousePosition.X, dev_state->dwMousePosition.Y);

		toadd = tmp;
		nread = 6;
	      }
	  }
	  break;

	case FOCUS_EVENT:
	  if (dev_state->use_focus) {
	    if (input_rec.Event.FocusEvent.bSetFocus)
	      sprintf (tmp, "\033[I");
	    else
	      sprintf (tmp, "\033[O");

	    toadd = tmp;
	    nread = 3;
	  }
	  break;

	case WINDOW_BUFFER_SIZE_EVENT:
	  send_winch_maybe ();
	  /* fall through */
	default:
	  continue;
	}

      if (toadd)
	{
	  line_edit_status res = line_edit (toadd, nread, ti);
	  if (res == line_edit_signalled)
	    goto sig_exit;
	  else if (res == line_edit_input_done)
	    break;
	}
    }

  while (buflen)
    if ((ch = get_readahead ()) < 0)
      break;
    else
      {
	buf[copied_chars++] = (unsigned char)(ch & 0xff);
	buflen--;
      }
#undef buf

  buflen = copied_chars;
  return;

err:
  __seterrno ();
  buflen = (size_t) -1;
  return;

sig_exit:
  set_sig_errno (EINTR);
  buflen = (size_t) -1;
}

void
fhandler_console::set_input_state ()
{
  if (tc->rstcons ())
    input_tcsetattr (0, &tc->ti);
}

bool
dev_console::fillin_info (HANDLE h)
{
  bool ret;
  CONSOLE_SCREEN_BUFFER_INFO linfo;

  if ((ret = GetConsoleScreenBufferInfo (h, &linfo)))
    {
      info.winTop = linfo.srWindow.Top;
      info.winBottom = linfo.srWindow.Bottom;
      info.dwWinSize.Y = 1 + linfo.srWindow.Bottom - linfo.srWindow.Top;
      info.dwWinSize.X = 1 + linfo.srWindow.Right - linfo.srWindow.Left;
      info.dwBufferSize = linfo.dwSize;
      info.dwCursorPosition = linfo.dwCursorPosition;
      info.wAttributes = linfo.wAttributes;
    }
  else
    {
      memset (&info, 0, sizeof info);
      info.dwWinSize.Y = 25;
      info.dwWinSize.X = 80;
      info.winBottom = 24;
    }

  return ret;
}

void
fhandler_console::scroll_screen (int x1, int y1, int x2, int y2, int xn, int yn)
{
  SMALL_RECT sr1, sr2;
  CHAR_INFO fill;
  COORD dest;

  dev_state->fillin_info (get_output_handle ());
  sr1.Left = x1 >= 0 ? x1 : dev_state->info.dwWinSize.X - 1;
  if (y1 == 0)
    sr1.Top = dev_state->info.winTop;
  else
    sr1.Top = y1 > 0 ? y1 : dev_state->info.winBottom;
  sr1.Right = x2 >= 0 ? x2 : dev_state->info.dwWinSize.X - 1;
  if (y2 == 0)
    sr1.Bottom = dev_state->info.winTop;
  else
    sr1.Bottom = y2 > 0 ? y2 : dev_state->info.winBottom;
  sr2.Top = srTop;
  sr2.Left = 0;
  sr2.Bottom = srBottom;
  sr2.Right = dev_state->info.dwWinSize.X - 1;
  if (sr1.Bottom > sr2.Bottom && sr1.Top <= sr2.Bottom)
    sr1.Bottom = sr2.Bottom;
  dest.X = xn >= 0 ? xn : dev_state->info.dwWinSize.X - 1;
  if (yn == 0)
    dest.Y = dev_state->info.winTop;
  else
    dest.Y = yn > 0 ? yn : dev_state->info.winBottom;
  fill.Char.AsciiChar = ' ';
  fill.Attributes = dev_state->current_win32_attr;
  ScrollConsoleScreenBuffer (get_output_handle (), &sr1, &sr2, dest, &fill);

  /* ScrollConsoleScreenBuffer on Windows 95 is buggy - when scroll distance
   * is more than half of screen, filling doesn't work as expected */

  if (sr1.Top == sr1.Bottom)
    /* nothing to do */;
  else if (dest.Y <= sr1.Top)	/* forward scroll */
    clear_screen (0, 1 + dest.Y + sr1.Bottom - sr1.Top, sr2.Right, sr2.Bottom);
  else			/* reverse scroll */
    clear_screen (0, sr1.Top, sr2.Right, dest.Y - 1);
}

int
fhandler_console::open (int flags, mode_t)
{
  HANDLE h;

  tcinit (get_tty_stuff (flags), false);

  set_io_handle (NULL);
  set_output_handle (NULL);

  set_flags ((flags & ~O_TEXT) | O_BINARY);

  /* Open the input handle as handle_ */
  h = CreateFile ("CONIN$", GENERIC_READ | GENERIC_WRITE,
		  FILE_SHARE_READ | FILE_SHARE_WRITE, sec_none_cloexec (flags),
		  OPEN_EXISTING, 0, 0);

  if (h == INVALID_HANDLE_VALUE)
    {
      __seterrno ();
      return 0;
    }
  set_io_handle (h);
  uninterruptible_io (true);	// Handled explicitly in read code

  h = CreateFile ("CONOUT$", GENERIC_READ | GENERIC_WRITE,
		  FILE_SHARE_READ | FILE_SHARE_WRITE, sec_none_cloexec (flags),
		  OPEN_EXISTING, 0, 0);

  if (h == INVALID_HANDLE_VALUE)
    {
      __seterrno ();
      return 0;
    }
  set_output_handle (h);

  if (dev_state->fillin_info (get_output_handle ()))
    {
      dev_state->current_win32_attr = dev_state->info.wAttributes;
      if (!dev_state->default_color)
	dev_state->default_color = dev_state->info.wAttributes;
      dev_state->set_default_attr ();
    }

  tc->rstcons (false);
  set_open_status ();
  cygheap->manage_console_count ("fhandler_console::open", 1);

  DWORD cflags;
  if (GetConsoleMode (get_io_handle (), &cflags))
    SetConsoleMode (get_io_handle (),
		    ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT | cflags);

  debug_printf ("opened conin$ %p, conout$ %p", get_io_handle (),
		get_output_handle ());

  return 1;
}

int
fhandler_console::close ()
{
  CloseHandle (get_io_handle ());
  CloseHandle (get_output_handle ());
  if (!hExeced)
    cygheap->manage_console_count ("fhandler_console::close", -1);
  return 0;
}

/*  Special console dup to duplicate input and output  handles.  */

int
fhandler_console::dup (fhandler_base *child)
{
  fhandler_console *fhc = (fhandler_console *) child;

  if (!fhc->open (get_flags () & ~O_NOCTTY, 0))
    system_printf ("error opening console, %E");

  return 0;
}

int
fhandler_console::ioctl (unsigned int cmd, void *buf)
{
  switch (cmd)
    {
      case TIOCGWINSZ:
	int st;

	st = dev_state->fillin_info (get_output_handle ());
	if (st)
	  {
	    /* *not* the buffer size, the actual screen size... */
	    /* based on Left Top Right Bottom of srWindow */
	    ((struct winsize *) buf)->ws_row = dev_state->info.dwWinSize.Y;
	    ((struct winsize *) buf)->ws_col = dev_state->info.dwWinSize.X;
	    syscall_printf ("WINSZ: (row=%d,col=%d)",
			   ((struct winsize *) buf)->ws_row,
			   ((struct winsize *) buf)->ws_col);
	    return 0;
	  }
	else
	  {
	    syscall_printf ("WINSZ failed");
	    __seterrno ();
	    return -1;
	  }
	return 0;
      case TIOCSWINSZ:
	bg_check (SIGTTOU);
	return 0;
      case KDGKBMETA:
	*(int *) buf = (dev_state->metabit) ? K_METABIT : K_ESCPREFIX;
	return 0;
      case KDSKBMETA:
	if ((int) buf == K_METABIT)
	  dev_state->metabit = TRUE;
	else if ((int) buf == K_ESCPREFIX)
	  dev_state->metabit = FALSE;
	else
	  {
	    set_errno (EINVAL);
	    return -1;
	  }
	return 0;
      case TIOCLINUX:
	if (*(unsigned char *) buf == 6)
	  {
	    *(unsigned char *) buf = (unsigned char) dev_state->nModifiers;
	    return 0;
	  }
	else
	  {
	    set_errno (EINVAL);
	    return -1;
	  }
    }

  return fhandler_base::ioctl (cmd, buf);
}

int
fhandler_console::tcflush (int queue)
{
  int res = 0;
  if (queue == TCIFLUSH
      || queue == TCIOFLUSH)
    {
      if (!FlushConsoleInputBuffer (get_io_handle ()))
	{
	  __seterrno ();
	  res = -1;
	}
    }
  return res;
}

int
fhandler_console::output_tcsetattr (int, struct termios const *t)
{
  /* All the output bits we can ignore */

  DWORD flags = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;

  int res = SetConsoleMode (get_output_handle (), flags) ? 0 : -1;
  syscall_printf ("%d = tcsetattr (,%x) (ENABLE FLAGS %x) (lflag %x oflag %x)",
		  res, t, flags, t->c_lflag, t->c_oflag);
  return res;
}

int
fhandler_console::input_tcsetattr (int, struct termios const *t)
{
  /* Ignore the optional_actions stuff, since all output is emitted
     instantly */

  DWORD oflags;

  if (!GetConsoleMode (get_io_handle (), &oflags))
    oflags = 0;
  DWORD flags = 0;

#if 0
  /* Enable/disable LF -> CRLF conversions */
  rbinary ((t->c_iflag & INLCR) ? false : true);
#endif

  /* There's some disparity between what we need and what's
     available.  We've got ECHO and ICANON, they've
     got ENABLE_ECHO_INPUT and ENABLE_LINE_INPUT. */

  tc->ti = *t;

  if (t->c_lflag & ECHO)
    {
      flags |= ENABLE_ECHO_INPUT;
    }
  if (t->c_lflag & ICANON)
    {
      flags |= ENABLE_LINE_INPUT;
    }

  if (flags & ENABLE_ECHO_INPUT
      && !(flags & ENABLE_LINE_INPUT))
    {
      /* This is illegal, so turn off the echo here, and fake it
	 when we read the characters */

      flags &= ~ENABLE_ECHO_INPUT;
    }

  if (t->c_lflag & ISIG)
    {
      flags |= ENABLE_PROCESSED_INPUT;
    }

  if (use_tty)
    {
      flags = 0; // ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
      tc->ti.c_iflag = 0;
      tc->ti.c_lflag = 0;
    }

  flags |= ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT;

  int res;
  if (flags == oflags)
    res = 0;
  else
    {
      res = SetConsoleMode (get_io_handle (), flags) ? 0 : -1;
      if (res < 0)
	__seterrno ();
      syscall_printf ("%d = tcsetattr (,%x) enable flags %p, c_lflag %p iflag %p",
		      res, t, flags, t->c_lflag, t->c_iflag);
    }

  tc->rstcons (false);
  return res;
}

int
fhandler_console::tcsetattr (int a, struct termios const *t)
{
  int res = output_tcsetattr (a, t);
  if (res != 0)
    return res;
  return input_tcsetattr (a, t);
}

int
fhandler_console::tcgetattr (struct termios *t)
{
  int res;
  *t = tc->ti;

  t->c_cflag |= CS8;

  DWORD flags;

  if (!GetConsoleMode (get_io_handle (), &flags))
    {
      __seterrno ();
      res = -1;
    }
  else
    {
      if (flags & ENABLE_ECHO_INPUT)
	t->c_lflag |= ECHO;

      if (flags & ENABLE_LINE_INPUT)
	t->c_lflag |= ICANON;

      if (flags & ENABLE_PROCESSED_INPUT)
	t->c_lflag |= ISIG;

      /* What about ENABLE_WINDOW_INPUT
	 and ENABLE_MOUSE_INPUT   ? */

      /* All the output bits we can ignore */
      res = 0;
    }
  syscall_printf ("%d = tcgetattr (%p) enable flags %p, t->lflag %p, t->iflag %p",
		 res, t, flags, t->c_lflag, t->c_iflag);
  return res;
}

fhandler_console::fhandler_console () :
  fhandler_termios ()
{
  trunc_buf.len = 0;
}

void
dev_console::set_color (HANDLE h)
{
  WORD win_fg = fg;
  WORD win_bg = bg;
  if (reverse)
    {
      WORD save_fg = win_fg;
      win_fg = (win_bg & BACKGROUND_RED   ? FOREGROUND_RED   : 0) |
	       (win_bg & BACKGROUND_GREEN ? FOREGROUND_GREEN : 0) |
	       (win_bg & BACKGROUND_BLUE  ? FOREGROUND_BLUE  : 0) |
	       (win_bg & BACKGROUND_INTENSITY ? FOREGROUND_INTENSITY : 0);
      win_bg = (save_fg & FOREGROUND_RED   ? BACKGROUND_RED   : 0) |
	       (save_fg & FOREGROUND_GREEN ? BACKGROUND_GREEN : 0) |
	       (save_fg & FOREGROUND_BLUE  ? BACKGROUND_BLUE  : 0) |
	       (save_fg & FOREGROUND_INTENSITY ? BACKGROUND_INTENSITY : 0);
    }

  /* apply attributes */
  if (underline)
    win_fg = underline_color;
  /* emulate blink with bright background */
  if (blink)
    win_bg |= BACKGROUND_INTENSITY;
  if (intensity == INTENSITY_INVISIBLE)
    win_fg = win_bg;
  else if (intensity != INTENSITY_BOLD)
    /* nothing to do */;
    /* apply foreground intensity only in non-reverse mode! */
  else if (reverse)
    win_bg |= BACKGROUND_INTENSITY;
  else
    win_fg |= FOREGROUND_INTENSITY;

  current_win32_attr = win_fg | win_bg;
  if (h)
    SetConsoleTextAttribute (h, current_win32_attr);
}

#define FOREGROUND_ATTR_MASK (FOREGROUND_RED | FOREGROUND_GREEN | \
			      FOREGROUND_BLUE | FOREGROUND_INTENSITY)
#define BACKGROUND_ATTR_MASK (BACKGROUND_RED | BACKGROUND_GREEN | \
			      BACKGROUND_BLUE | BACKGROUND_INTENSITY)
void
dev_console::set_default_attr ()
{
  blink = underline = reverse = false;
  intensity = INTENSITY_NORMAL;
  fg = default_color & FOREGROUND_ATTR_MASK;
  bg = default_color & BACKGROUND_ATTR_MASK;
  set_color (NULL);
}

/*
 * Clear the screen context from x1/y1 to x2/y2 cell.
 * Negative values represents current screen dimensions
 */
void
fhandler_console::clear_screen (int x1, int y1, int x2, int y2)
{
  COORD tlc;
  DWORD done;
  int num;

  dev_state->fillin_info (get_output_handle ());

  if (x1 < 0)
    x1 = dev_state->info.dwWinSize.X - 1;
  if (y1 < 0)
    y1 = dev_state->info.winBottom;
  if (x2 < 0)
    x2 = dev_state->info.dwWinSize.X - 1;
  if (y2 < 0)
    y2 = dev_state->info.winBottom;

  num = abs (y1 - y2) * dev_state->info.dwBufferSize.X + abs (x1 - x2) + 1;

  if ((y2 * dev_state->info.dwBufferSize.X + x2) > (y1 * dev_state->info.dwBufferSize.X + x1))
    {
      tlc.X = x1;
      tlc.Y = y1;
    }
  else
    {
      tlc.X = x2;
      tlc.Y = y2;
    }
  FillConsoleOutputCharacterA (get_output_handle (), ' ',
			       num,
			       tlc,
			       &done);
  FillConsoleOutputAttribute (get_output_handle (),
			       dev_state->current_win32_attr,
			       num,
			       tlc,
			       &done);
}

void
fhandler_console::cursor_set (bool rel_to_top, int x, int y)
{
  COORD pos;

  dev_state->fillin_info (get_output_handle ());
  if (y > dev_state->info.winBottom)
    y = dev_state->info.winBottom;
  else if (y < 0)
    y = 0;
  else if (rel_to_top)
    y += dev_state->info.winTop;

  if (x > dev_state->info.dwWinSize.X)
    x = dev_state->info.dwWinSize.X - 1;
  else if (x < 0)
    x = 0;

  pos.X = x;
  pos.Y = y;
  SetConsoleCursorPosition (get_output_handle (), pos);
}

void
fhandler_console::cursor_rel (int x, int y)
{
  dev_state->fillin_info (get_output_handle ());
  x += dev_state->info.dwCursorPosition.X;
  y += dev_state->info.dwCursorPosition.Y;
  cursor_set (false, x, y);
}

void
fhandler_console::cursor_get (int *x, int *y)
{
  dev_state->fillin_info (get_output_handle ());
  *y = dev_state->info.dwCursorPosition.Y;
  *x = dev_state->info.dwCursorPosition.X;
}

/* VT100 line drawing graphics mode maps `abcdefghijklmnopqrstuvwxyz{|}~ to
   graphical characters */
static wchar_t __vt100_conv [31] = {
	0x25C6, /* Black Diamond */
	0x2592, /* Medium Shade */
	0x2409, /* Symbol for Horizontal Tabulation */
	0x240C, /* Symbol for Form Feed */
	0x240D, /* Symbol for Carriage Return */
	0x240A, /* Symbol for Line Feed */
	0x00B0, /* Degree Sign */
	0x00B1, /* Plus-Minus Sign */
	0x2424, /* Symbol for Newline */
	0x240B, /* Symbol for Vertical Tabulation */
	0x2518, /* Box Drawings Light Up And Left */
	0x2510, /* Box Drawings Light Down And Left */
	0x250C, /* Box Drawings Light Down And Right */
	0x2514, /* Box Drawings Light Up And Right */
	0x253C, /* Box Drawings Light Vertical And Horizontal */
	0x23BA, /* Horizontal Scan Line-1 */
	0x23BB, /* Horizontal Scan Line-3 */
	0x2500, /* Box Drawings Light Horizontal */
	0x23BC, /* Horizontal Scan Line-7 */
	0x23BD, /* Horizontal Scan Line-9 */
	0x251C, /* Box Drawings Light Vertical And Right */
	0x2524, /* Box Drawings Light Vertical And Left */
	0x2534, /* Box Drawings Light Up And Horizontal */
	0x252C, /* Box Drawings Light Down And Horizontal */
	0x2502, /* Box Drawings Light Vertical */
	0x2264, /* Less-Than Or Equal To */
	0x2265, /* Greater-Than Or Equal To */
	0x03C0, /* Greek Small Letter Pi */
	0x2260, /* Not Equal To */
	0x00A3, /* Pound Sign */
	0x00B7, /* Middle Dot */
};

inline
bool fhandler_console::write_console (PWCHAR buf, DWORD len, DWORD& done)
{
  if (dev_state->iso_2022_G1
	? dev_state->vt100_graphics_mode_G1
	: dev_state->vt100_graphics_mode_G0)
    for (DWORD i = 0; i < len; i ++)
      if (buf[i] >= (unsigned char) '`' && buf[i] <= (unsigned char) '~')
        buf[i] = __vt100_conv[buf[i] - (unsigned char) '`'];

  while (len > 0)
    {
      DWORD nbytes = len > MAX_WRITE_CHARS ? MAX_WRITE_CHARS : len;
      if (!WriteConsoleW (get_output_handle (), buf, nbytes, &done, 0))
	{
	  __seterrno ();
	  return false;
	}
      len -= done;
      buf += done;
    }
  return true;
}

#define BAK 1
#define ESC 2
#define NOR 0
#define IGN 4
#if 1
#define ERR 5
#else
#define ERR NOR
#endif
#define DWN 6
#define BEL 7
#define TAB 8 /* We should't let the console deal with these */
#define CR 13
#define LF 10
#define SO 14
#define SI 15

static const char base_chars[256] =
{
/*00 01 02 03 04 05 06 07 */ IGN, ERR, ERR, NOR, NOR, NOR, NOR, BEL,
/*08 09 0A 0B 0C 0D 0E 0F */ BAK, TAB, DWN, ERR, ERR, CR,  SO,  SI,
/*10 11 12 13 14 15 16 17 */ NOR, NOR, ERR, ERR, ERR, ERR, ERR, ERR,
/*18 19 1A 1B 1C 1D 1E 1F */ NOR, NOR, ERR, ESC, ERR, ERR, ERR, ERR,
/*   !  "  #  $  %  &  '  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*(  )  *  +  ,  -  .  /  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*0  1  2  3  4  5  6  7  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*8  9  :  ;  <  =  >  ?  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*@  A  B  C  D  E  F  G  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*H  I  J  K  L  M  N  O  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*P  Q  R  S  T  U  V  W  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*X  Y  Z  [  \  ]  ^  _  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*`  a  b  c  d  e  f  g  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*h  i  j  k  l  m  n  o  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*p  q  r  s  t  u  v  w  */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*x  y  z  {  |  }  ~  7F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*80 81 82 83 84 85 86 87 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*88 89 8A 8B 8C 8D 8E 8F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*90 91 92 93 94 95 96 97 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*98 99 9A 9B 9C 9D 9E 9F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*A0 A1 A2 A3 A4 A5 A6 A7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*A8 A9 AA AB AC AD AE AF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*B0 B1 B2 B3 B4 B5 B6 B7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*B8 B9 BA BB BC BD BE BF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*C0 C1 C2 C3 C4 C5 C6 C7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*C8 C9 CA CB CC CD CE CF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*D0 D1 D2 D3 D4 D5 D6 D7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*D8 D9 DA DB DC DD DE DF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*E0 E1 E2 E3 E4 E5 E6 E7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*E8 E9 EA EB EC ED EE EF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*F0 F1 F2 F3 F4 F5 F6 F7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
/*F8 F9 FA FB FC FD FE FF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR };

void
fhandler_console::char_command (char c)
{
  int x, y;
  char buf[40];

  switch (c)
    {
    case 'm':   /* Set Graphics Rendition */
       for (int i = 0; i <= dev_state->nargs_; i++)
	 switch (dev_state->args_[i])
	   {
	     case 0:    /* normal color */
	       dev_state->set_default_attr ();
	       break;
	     case 1:    /* bold */
	       dev_state->intensity = INTENSITY_BOLD;
	       break;
	     case 2:	/* dim */
	       dev_state->intensity = INTENSITY_DIM;
	       break;
	     case 4:	/* underlined */
	       dev_state->underline = 1;
	       break;
	     case 5:    /* blink mode */
	       dev_state->blink = true;
	       break;
	     case 7:    /* reverse */
	       dev_state->reverse = true;
	       break;
	     case 8:    /* invisible */
	       dev_state->intensity = INTENSITY_INVISIBLE;
	       break;
	     case 10:   /* end alternate charset */
	       dev_state->alternate_charset_active = false;
	       break;
	     case 11:   /* start alternate charset */
	       dev_state->alternate_charset_active = true;
	       break;
	     case 22:
	     case 28:
	       dev_state->intensity = INTENSITY_NORMAL;
	       break;
	     case 24:
	       dev_state->underline = false;
	       break;
	     case 25:
	       dev_state->blink = false;
	       break;
	     case 27:
	       dev_state->reverse = false;
	       break;
	     case 30:		/* BLACK foreground */
	       dev_state->fg = 0;
	       break;
	     case 31:		/* RED foreground */
	       dev_state->fg = FOREGROUND_RED;
	       break;
	     case 32:		/* GREEN foreground */
	       dev_state->fg = FOREGROUND_GREEN;
	       break;
	     case 33:		/* YELLOW foreground */
	       dev_state->fg = FOREGROUND_RED | FOREGROUND_GREEN;
	       break;
	     case 34:		/* BLUE foreground */
	       dev_state->fg = FOREGROUND_BLUE;
	       break;
	     case 35:		/* MAGENTA foreground */
	       dev_state->fg = FOREGROUND_RED | FOREGROUND_BLUE;
	       break;
	     case 36:		/* CYAN foreground */
	       dev_state->fg = FOREGROUND_BLUE | FOREGROUND_GREEN;
	       break;
	     case 37:		/* WHITE foreg */
	       dev_state->fg = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
	       break;
	     case 39:
	       dev_state->fg = dev_state->default_color & FOREGROUND_ATTR_MASK;
	       break;
	     case 40:		/* BLACK background */
	       dev_state->bg = 0;
	       break;
	     case 41:		/* RED background */
	       dev_state->bg = BACKGROUND_RED;
	       break;
	     case 42:		/* GREEN background */
	       dev_state->bg = BACKGROUND_GREEN;
	       break;
	     case 43:		/* YELLOW background */
	       dev_state->bg = BACKGROUND_RED | BACKGROUND_GREEN;
	       break;
	     case 44:		/* BLUE background */
	       dev_state->bg = BACKGROUND_BLUE;
	       break;
	     case 45:		/* MAGENTA background */
	       dev_state->bg = BACKGROUND_RED | BACKGROUND_BLUE;
	       break;
	     case 46:		/* CYAN background */
	       dev_state->bg = BACKGROUND_BLUE | BACKGROUND_GREEN;
	       break;
	     case 47:    /* WHITE background */
	       dev_state->bg = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
	       break;
	     case 49:
	       dev_state->bg = dev_state->default_color & BACKGROUND_ATTR_MASK;
	       break;
	   }
       dev_state->set_color (get_output_handle ());
      break;
    case 'h':
    case 'l':
      if (!dev_state->saw_question_mark)
	{
	  switch (dev_state->args_[0])
	    {
	    case 4:    /* Insert mode */
	      dev_state->insert_mode = (c == 'h') ? true : false;
	      syscall_printf ("insert mode %sabled", dev_state->insert_mode ? "en" : "dis");
	      break;
	    }
	  break;
	}
      switch (dev_state->args_[0])
	{
	case 47:   /* Save/Restore screen */
	  if (c == 'h') /* save */
	    {
	      CONSOLE_SCREEN_BUFFER_INFO now;
	      COORD cob = { 0, 0 };

	      if (!GetConsoleScreenBufferInfo (get_output_handle (), &now))
		break;

	      dev_state->savebufsiz.X = now.srWindow.Right - now.srWindow.Left + 1;
	      dev_state->savebufsiz.Y = now.srWindow.Bottom - now.srWindow.Top + 1;

	      if (dev_state->savebuf)
		cfree (dev_state->savebuf);
	      dev_state->savebuf = (PCHAR_INFO) cmalloc_abort (HEAP_1_BUF, sizeof (CHAR_INFO) *
					     dev_state->savebufsiz.X * dev_state->savebufsiz.Y);

	      ReadConsoleOutputW (get_output_handle (), dev_state->savebuf,
				  dev_state->savebufsiz, cob, &now.srWindow);
	    }
	  else		/* restore */
	    {
	      CONSOLE_SCREEN_BUFFER_INFO now;
	      COORD cob = { 0, 0 };

	      if (!GetConsoleScreenBufferInfo (get_output_handle (), &now))
		break;

	      if (!dev_state->savebuf)
		break;

	      WriteConsoleOutputW (get_output_handle (), dev_state->savebuf,
				   dev_state->savebufsiz, cob, &now.srWindow);

	      cfree (dev_state->savebuf);
	      dev_state->savebuf = NULL;
	      dev_state->savebufsiz.X = dev_state->savebufsiz.Y = 0;
	    }
	  break;

	case 67: /* DECBKM ("DEC Backarrow Key Mode") */
	  dev_state->backspace_keycode = (c == 'h' ? CTRL('H') : CERASE);
	  break;

	case 1000: /* Mouse tracking */
	  dev_state->use_mouse = (c == 'h') ? 1 : 0;
	  syscall_printf ("mouse support set to mode %d", dev_state->use_mouse);
	  break;

	case 1002: /* Mouse button event tracking */
	  dev_state->use_mouse = (c == 'h') ? 2 : 0;
	  syscall_printf ("mouse support set to mode %d", dev_state->use_mouse);
	  break;

	case 1003: /* Mouse any event tracking */
	  dev_state->use_mouse = (c == 'h') ? 3 : 0;
	  syscall_printf ("mouse support set to mode %d", dev_state->use_mouse);
	  break;

	case 1004: /* Focus in/out event reporting */
	  dev_state->use_focus = (c == 'h') ? true : false;
	  syscall_printf ("focus reporting set to %d", dev_state->use_focus);
	  break;

	case 2000: /* Raw keyboard mode */
	  set_raw_win32_keyboard_mode ((c == 'h') ? true : false);
	  break;

	default: /* Ignore */
	  syscall_printf ("unknown h/l command: %d", dev_state->args_[0]);
	  break;
	}
      break;
    case 'J':
      switch (dev_state->args_[0])
	{
	case 0:			/* Clear to end of screen */
	  cursor_get (&x, &y);
	  clear_screen (x, y, -1, -1);
	  break;
	case 1:			/* Clear from beginning of screen to cursor */
	  cursor_get (&x, &y);
	  clear_screen (0, 0, x, y);
	  break;
	case 2:			/* Clear screen */
	  clear_screen (0, 0, -1, -1);
	  cursor_set (true, 0,0);
	  break;
	default:
	  goto bad_escape;
	}
      break;

    case 'A':
      cursor_rel (0, -(dev_state->args_[0] ? dev_state->args_[0] : 1));
      break;
    case 'B':
      cursor_rel (0, dev_state->args_[0] ? dev_state->args_[0] : 1);
      break;
    case 'C':
      cursor_rel (dev_state->args_[0] ? dev_state->args_[0] : 1, 0);
      break;
    case 'D':
      cursor_rel (-(dev_state->args_[0] ? dev_state->args_[0] : 1),0);
      break;
    case 'K':
      switch (dev_state->args_[0])
	{
	  case 0:		/* Clear to end of line */
	    cursor_get (&x, &y);
	    clear_screen (x, y, -1, y);
	    break;
	  case 2:		/* Clear line */
	    cursor_get (&x, &y);
	    clear_screen (0, y, -1, y);
	    break;
	  case 1:		/* Clear from bol to cursor */
	    cursor_get (&x, &y);
	    clear_screen (0, y, x, y);
	    break;
	  default:
	    goto bad_escape;
	}
      break;
    case 'H':
    case 'f':
      cursor_set (true, (dev_state->args_[1] ? dev_state->args_[1] : 1) - 1,
			(dev_state->args_[0] ? dev_state->args_[0] : 1) - 1);
      break;
    case 'G':   /* hpa - position cursor at column n - 1 */
      cursor_get (&x, &y);
      cursor_set (false, (dev_state->args_[0] ? dev_state->args_[0] - 1 : 0), y);
      break;
    case 'd':   /* vpa - position cursor at line n */
      cursor_get (&x, &y);
      cursor_set (true, x, (dev_state->args_[0] ? dev_state->args_[0] - 1 : 0));
      break;
    case 's':   /* Save cursor position */
      cursor_get (&dev_state->savex, &dev_state->savey);
      dev_state->savey -= dev_state->info.winTop;
      break;
    case 'u':   /* Restore cursor position */
      cursor_set (true, dev_state->savex, dev_state->savey);
      break;
    case 'I':	/* TAB */
      cursor_get (&x, &y);
      cursor_set (false, 8 * (x / 8 + 1), y);
      break;
    case 'L':				/* AL - insert blank lines */
      dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
      cursor_get (&x, &y);
      scroll_screen (0, y, -1, -1, 0, y + dev_state->args_[0]);
      break;
    case 'M':				/* DL - delete lines */
      dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
      cursor_get (&x, &y);
      scroll_screen (0, y + dev_state->args_[0], -1, -1, 0, y);
      break;
    case '@':				/* IC - insert chars */
      dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
      cursor_get (&x, &y);
      scroll_screen (x, y, -1, y, x + dev_state->args_[0], y);
      break;
    case 'P':				/* DC - delete chars */
      dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
      cursor_get (&x, &y);
      scroll_screen (x + dev_state->args_[0], y, -1, y, x, y);
      break;
    case 'S':				/* SF - Scroll forward */
      dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
      scroll_screen (0, dev_state->args_[0], -1, -1, 0, 0);
      break;
    case 'T':				/* SR - Scroll down */
      dev_state->fillin_info (get_output_handle ());
      dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
      scroll_screen (0, 0, -1, -1, 0, dev_state->info.winTop + dev_state->args_[0]);
      break;
    case 'X':				/* ec - erase chars */
      dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
      cursor_get (&x, &y);
      scroll_screen (x + dev_state->args_[0], y, -1, y, x, y);
      scroll_screen (x, y, -1, y, x + dev_state->args_[0], y);
      break;
    case 'Z':				/* Back tab */
      cursor_get (&x, &y);
      cursor_set (false, ((8 * (x / 8 + 1)) - 8), y);
      break;
    case 'b':				/* Repeat char #1 #2 times */
      if (dev_state->insert_mode)
	{
	  cursor_get (&x, &y);
	  scroll_screen (x, y, -1, y, x + dev_state->args_[1], y);
	}
      while (dev_state->args_[1]--)
	WriteFile (get_output_handle (), &dev_state->args_[0], 1, (DWORD *) &x, 0);
      break;
    case 'c':				/* u9 - Terminal enquire string */
      if (dev_state->saw_greater_than_sign)
	/* Generate Secondary Device Attribute report, using 67 = ASCII 'C' 
	   to indicate Cygwin (convention used by Rxvt, Urxvt, Screen, Mintty), 
	   and cygwin version for terminal version. */
	__small_sprintf (buf, "\033[>67;%d%02d;0c", CYGWIN_VERSION_DLL_MAJOR, CYGWIN_VERSION_DLL_MINOR);
      else
	strcpy (buf, "\033[?6c");
      /* The generated report needs to be injected for read-ahead into the 
         fhandler_console object associated with standard input.
         The current call does not work. */
      puts_readahead (buf);
      break;
    case 'n':
      switch (dev_state->args_[0])
	{
	case 6:				/* u7 - Cursor position request */
	  cursor_get (&x, &y);
	  y -= dev_state->info.winTop;
	  /* x -= dev_state->info.winLeft;		// not available yet */
	  __small_sprintf (buf, "\033[%d;%dR", y + 1, x + 1);
	  puts_readahead (buf);
	  break;
    default:
	  goto bad_escape;
	}
      break;
    case 'r':				/* Set Scroll region */
      dev_state->scroll_region.Top = dev_state->args_[0] ? dev_state->args_[0] - 1 : 0;
      dev_state->scroll_region.Bottom = dev_state->args_[1] ? dev_state->args_[1] - 1 : -1;
      cursor_set (true, 0, 0);
      break;
    case 'g':				/* TAB set/clear */
      break;
    default:
bad_escape:
      break;
    }
}

/* This gets called when we found an invalid input character.  We just
   print a half filled square (UTF 0x2592).  We have no chance to figure
   out the "meaning" of the input char anyway. */
inline void
fhandler_console::write_replacement_char ()
{
  static const wchar_t replacement_char = 0x2592; /* Half filled square */
  DWORD done;
  WriteConsoleW (get_output_handle (), &replacement_char, 1, &done, 0);
}

const unsigned char *
fhandler_console::write_normal (const unsigned char *src,
				const unsigned char *end)
{
  /* Scan forward to see what a char which needs special treatment */
  DWORD done;
  DWORD buf_len;
  const unsigned char *found = src;
  size_t ret;
  mbstate_t ps;
  UINT cp = dev_state->get_console_cp ();
  const char *charset;
  mbtowc_p f_mbtowc;

  if (cp)
    {
      /* The alternate charset is always 437, just as in the Linux console. */
      f_mbtowc = __cp_mbtowc;
      charset = "CP437";
    }
  else
    {
      f_mbtowc = cygheap->locale.mbtowc;
      charset = cygheap->locale.charset;
    }

  /* First check if we have cached lead bytes of a former try to write
     a truncated multibyte sequence.  If so, process it. */
  if (trunc_buf.len)
    {
      const unsigned char *nfound;
      int cp_len = min (end - src, 4 - trunc_buf.len);
      memcpy (trunc_buf.buf + trunc_buf.len, src, cp_len);
      memset (&ps, 0, sizeof ps);
      switch (ret = f_mbtowc (_REENT, NULL, (const char *) trunc_buf.buf,
			       trunc_buf.len + cp_len, charset, &ps))
	{
	case -2:
	  /* Still truncated multibyte sequence?  Keep in trunc_buf. */
	  trunc_buf.len += cp_len;
	  return end;
	case -1:
	  /* Give up, print replacement chars for trunc_buf... */
	  for (int i = 0; i < trunc_buf.len; ++i)
	    write_replacement_char ();
	  /* ... mark trunc_buf as unused... */
	  trunc_buf.len = 0;
	  /* ... and proceed. */
	  nfound = NULL;
	  break;
	case 0:
	  nfound = trunc_buf.buf + 1;
	  break;
	default:
	  nfound = trunc_buf.buf + ret;
	  break;
	}
      /* Valid multibyte sequence?  Process. */
      if (nfound)
	{
	  buf_len = dev_state->str_to_con (f_mbtowc, charset, write_buf,
					   (const char *) trunc_buf.buf,
					   nfound - trunc_buf.buf);
	  if (!write_console (write_buf, buf_len, done))
	    {
	      debug_printf ("multibyte sequence write failed, handle %p", get_output_handle ());
	      return 0;
	    }
	  found = src + (nfound - trunc_buf.buf - trunc_buf.len);
	  trunc_buf.len = 0;
	  return found;
	}
    }

  /* Loop over src buffer as long as we have just simple characters.  Stop
     as soon as we reach the conversion limit, or if we encounter a control
     character or a truncated or invalid mutibyte sequence. */
  memset (&ps, 0, sizeof ps);
  while (found < end
	 && found - src < CONVERT_LIMIT
	 && base_chars[*found] == NOR)
    {
      switch (ret = f_mbtowc (_REENT, NULL, (const char *) found,
			       end - found, charset, &ps))
	{
	case -2: /* Truncated multibyte sequence.  Store for next write. */
	  trunc_buf.len = end - found;
	  memcpy (trunc_buf.buf, found, trunc_buf.len);
	  goto do_print;
	case -1: /* Invalid multibyte sequence. Handled below. */
	  goto do_print;
	case 0:
	  found++;
	  break;
	default:
	  found += ret;
	  break;
	}
    }

do_print:

  /* Print all the base characters out */
  if (found != src)
    {
      DWORD len = found - src;
      buf_len = dev_state->str_to_con (f_mbtowc, charset, write_buf,
				       (const char *) src, len);
      if (!buf_len)
	{
	  debug_printf ("conversion error, handle %p",
			get_output_handle ());
	  __seterrno ();
	  return 0;
	}

      if (dev_state->insert_mode)
	{
	  int x, y;
	  cursor_get (&x, &y);
	  scroll_screen (x, y, -1, y, x + buf_len, y);
	}

      if (!write_console (write_buf, buf_len, done))
	{
	  debug_printf ("write failed, handle %p", get_output_handle ());
	  return 0;
	}
      /* Stop here if we reached the conversion limit. */
      if (len >= CONVERT_LIMIT)
	return found + trunc_buf.len;
    }
  /* If there's still something in the src buffer, but it's not a truncated
     multibyte sequence, then we stumbled over a control character or an
     invalid multibyte sequence.  Print it. */
  if (found < end && trunc_buf.len == 0)
    {
      int x, y;
      switch (base_chars[*found])
	{
	case SO:	/* Shift Out: Invoke G1 character set (ISO 2022) */
	  dev_state->iso_2022_G1 = true;
	  break;
	case SI:	/* Shift In: Invoke G0 character set (ISO 2022) */
	  dev_state->iso_2022_G1 = false;
	  break;
	case BEL:
	  beep ();
	  break;
	case ESC:
	  dev_state->state_ = gotesc;
	  break;
	case DWN:
	  cursor_get (&x, &y);
	  if (y >= srBottom)
	    {
	      if (y >= dev_state->info.winBottom && !dev_state->scroll_region.Top)
		WriteConsoleW (get_output_handle (), L"\n", 1, &done, 0);
	      else
		{
		  scroll_screen (0, srTop + 1, -1, srBottom, 0, srTop);
		  y--;
		}
	    }
	  cursor_set (false, ((tc->ti.c_oflag & ONLCR) ? 0 : x), y + 1);
	  break;
	case BAK:
	  cursor_rel (-1, 0);
	  break;
	case IGN:
	  cursor_rel (1, 0);
	  break;
	case CR:
	  cursor_get (&x, &y);
	  cursor_set (false, 0, y);
	  break;
	case ERR:
	  /* Don't print chars marked as ERR chars, except for a ASCII CAN
	     sequence which is printed as singlebyte chars from the UTF
	     Basic Latin and Latin 1 Supplement plains. */
	  if (*found == 0x18)
	    {
	      write_replacement_char ();
	      if (found + 1 < end)
		{
		  ret = __utf8_mbtowc (_REENT, NULL, (const char *) found + 1,
				       end - found - 1, NULL, &ps);
		  if (ret != (size_t) -1)
		    while (ret-- > 0)
		      {
			WCHAR w = *(found + 1);
			WriteConsoleW (get_output_handle (), &w, 1, &done, 0);
			found++;
		      }
		}
	    }
	  break;
	case TAB:
	  cursor_get (&x, &y);
	  cursor_set (false, 8 * (x / 8 + 1), y);
	  break;
	case NOR:
	  write_replacement_char ();
	  break;
	}
      found++;
    }
  return found + trunc_buf.len;
}

ssize_t __stdcall
fhandler_console::write (const void *vsrc, size_t len)
{
  /* Run and check for ansi sequences */
  unsigned const char *src = (unsigned char *) vsrc;
  unsigned const char *end = src + len;
  /* This might look a bit far fetched, but using the TLS path buffer allows
     to allocate a big buffer without using the stack too much.  Doing it here
     in write instead of in write_normal should be faster, too. */
  tmp_pathbuf tp;
  write_buf = tp.w_get ();

  debug_printf ("%x, %d", vsrc, len);

  while (src < end)
    {
      debug_printf ("at %d(%c) state is %d", *src, isprint (*src) ? *src : ' ',
		    dev_state->state_);
      switch (dev_state->state_)
	{
	case normal:
	  src = write_normal (src, end);
	  if (!src) /* write_normal failed */
	    return -1;
	  break;
	case gotesc:
	  if (*src == '[')		/* CSI Control Sequence Introducer */
	    {
	      dev_state->state_ = gotsquare;
	      dev_state->saw_question_mark = false;
	      dev_state->saw_greater_than_sign = false;
	      for (dev_state->nargs_ = 0; dev_state->nargs_ < MAXARGS; dev_state->nargs_++)
		dev_state->args_[dev_state->nargs_] = 0;
	      dev_state->nargs_ = 0;
	    }
	  else if (*src == ']')		/* OSC Operating System Command */
	    {
	      dev_state->rarg = 0;
	      dev_state->my_title_buf[0] = '\0';
	      dev_state->state_ = gotrsquare;
	    }
	  else if (*src == '(')		/* Designate G0 character set */
	    {
	      dev_state->state_ = gotparen;
	    }
	  else if (*src == ')')		/* Designate G1 character set */
	    {
	      dev_state->state_ = gotrparen;
	    }
	  else if (*src == 'M')		/* Reverse Index (scroll down) */
	    {
	      dev_state->fillin_info (get_output_handle ());
	      scroll_screen (0, 0, -1, -1, 0, dev_state->info.winTop + 1);
	      dev_state->state_ = normal;
	    }
	  else if (*src == 'c')		/* RIS Full Reset */
	    {
	      dev_state->set_default_attr ();
	      dev_state->vt100_graphics_mode_G0 = false;
	      dev_state->vt100_graphics_mode_G1 = false;
	      dev_state->iso_2022_G1 = false;
	      clear_screen (0, 0, -1, -1);
	      cursor_set (true, 0, 0);
	      dev_state->state_ = normal;
	    }
	  else if (*src == '8')		/* DECRC Restore cursor position */
	    {
	      cursor_set (true, dev_state->savex, dev_state->savey);
	      dev_state->state_ = normal;
	    }
	  else if (*src == '7')		/* DECSC Save cursor position */
	    {
	      cursor_get (&dev_state->savex, &dev_state->savey);
	      dev_state->savey -= dev_state->info.winTop;
	      dev_state->state_ = normal;
	    }
	  else if (*src == 'R')		/* ? */
	      dev_state->state_ = normal;
	  else
	    {
	      dev_state->state_ = normal;
	    }
	  src++;
	  break;
	case gotarg1:
	  if (isdigit (*src))
	    {
	      dev_state->args_[dev_state->nargs_] = dev_state->args_[dev_state->nargs_] * 10 + *src - '0';
	      src++;
	    }
	  else if (*src == ';')
	    {
	      src++;
	      dev_state->nargs_++;
	      if (dev_state->nargs_ >= MAXARGS)
		dev_state->nargs_--;
	    }
	  else
	    {
	      dev_state->state_ = gotcommand;
	    }
	  break;
	case gotcommand:
	  char_command (*src++);
	  dev_state->state_ = normal;
	  break;
	case gotrsquare:
	  if (isdigit (*src))
	    dev_state->rarg = dev_state->rarg * 10 + (*src - '0');
	  else if (*src == ';' && (dev_state->rarg == 2 || dev_state->rarg == 0))
	    dev_state->state_ = gettitle;
	  else
	    dev_state->state_ = eattitle;
	  src++;
	  break;
	case eattitle:
	case gettitle:
	  {
	    int n = strlen (dev_state->my_title_buf);
	    if (*src < ' ')
	      {
		if (*src == '\007' && dev_state->state_ == gettitle)
		  {
		    if (old_title)
		      strcpy (old_title, dev_state->my_title_buf);
		    set_console_title (dev_state->my_title_buf);
		  }
		dev_state->state_ = normal;
	      }
	    else if (n < TITLESIZE)
	      {
		dev_state->my_title_buf[n++] = *src;
		dev_state->my_title_buf[n] = '\0';
	      }
	    src++;
	    break;
	  }
	case gotsquare:
	  if (*src == ';')
	    {
	      dev_state->state_ = gotarg1;
	      dev_state->nargs_++;
	      src++;
	    }
	  else if (isalpha (*src))
	    dev_state->state_ = gotcommand;
	  else if (*src != '@' && !isalpha (*src) && !isdigit (*src))
	    {
	      if (*src == '?')
		dev_state->saw_question_mark = true;
	      else if (*src == '>')
		dev_state->saw_greater_than_sign = true;
	      /* ignore any extra chars between [ and first arg or command */
	      src++;
	    }
	  else
	    dev_state->state_ = gotarg1;
	  break;
	case gotparen:	/* Designate G0 Character Set (ISO 2022) */
	  if (*src == '0')
	    dev_state->vt100_graphics_mode_G0 = true;
	  else
	    dev_state->vt100_graphics_mode_G0 = false;
	  dev_state->state_ = normal;
	  src++;
	  break;
	case gotrparen:	/* Designate G1 Character Set (ISO 2022) */
	  if (*src == '0')
	    dev_state->vt100_graphics_mode_G1 = true;
	  else
	    dev_state->vt100_graphics_mode_G1 = false;
	  dev_state->state_ = normal;
	  src++;
	  break;
	}
    }

  syscall_printf ("%d = fhandler_console::write (...)", len);

  return len;
}

static struct {
  int vk;
  const char *val[4];
} keytable[] NO_COPY = {
	       /* NORMAL */    /* SHIFT */     /* CTRL */     /* CTRL-SHIFT */
  /* Unmodified and Alt-modified keypad keys comply with linux console
     SHIFT, CTRL, CTRL-SHIFT modifiers comply with xterm modifier usage */
  {VK_NUMPAD5,	{"\033[G",	"\033[1;2G",	"\033[1;5G",	"\033[1;6G"}},
  {VK_CLEAR,	{"\033[G",	"\033[1;2G",	"\033[1;5G",	"\033[1;6G"}},
  {VK_LEFT,	{"\033[D",	"\033[1;2D",	"\033[1;5D",	"\033[1;6D"}},
  {VK_RIGHT,	{"\033[C",	"\033[1;2C",	"\033[1;5C",	"\033[1;6C"}},
  {VK_UP,	{"\033[A",	"\033[1;2A",	"\033[1;5A",	"\033[1;6A"}},
  {VK_DOWN,	{"\033[B",	"\033[1;2B",	"\033[1;5B",	"\033[1;6B"}},
  {VK_PRIOR,	{"\033[5~",	"\033[5;2~",	"\033[5;5~",	"\033[5;6~"}},
  {VK_NEXT,	{"\033[6~",	"\033[6;2~",	"\033[6;5~",	"\033[6;6~"}},
  {VK_HOME,	{"\033[1~",	"\033[1;2~",	"\033[1;5~",	"\033[1;6~"}},
  {VK_END,	{"\033[4~",	"\033[4;2~",	"\033[4;5~",	"\033[4;6~"}},
  {VK_INSERT,	{"\033[2~",	"\033[2;2~",	"\033[2;5~",	"\033[2;6~"}},
  {VK_DELETE,	{"\033[3~",	"\033[3;2~",	"\033[3;5~",	"\033[3;6~"}},
  /* F1...F12, SHIFT-F1...SHIFT-F10 comply with linux console
     F6...F12, and all modified F-keys comply with rxvt (compatible extension) */
  {VK_F1,	{"\033[[A",	"\033[23~",	"\033[11^",	"\033[23^"}},
  {VK_F2,	{"\033[[B",	"\033[24~",	"\033[12^",	"\033[24^"}},
  {VK_F3,	{"\033[[C",	"\033[25~",	"\033[13^",	"\033[25^"}},
  {VK_F4,	{"\033[[D",	"\033[26~",	"\033[14^",	"\033[26^"}},
  {VK_F5,	{"\033[[E",	"\033[28~",	"\033[15^",	"\033[28^"}},
  {VK_F6,	{"\033[17~",	"\033[29~",	"\033[17^",	"\033[29^"}},
  {VK_F7,	{"\033[18~",	"\033[31~",	"\033[18^",	"\033[31^"}},
  {VK_F8,	{"\033[19~",	"\033[32~",	"\033[19^",	"\033[32^"}},
  {VK_F9,	{"\033[20~",	"\033[33~",	"\033[20^",	"\033[33^"}},
  {VK_F10,	{"\033[21~",	"\033[34~",	"\033[21^",	"\033[34^"}},
  {VK_F11,	{"\033[23~",	"\033[23$",	"\033[23^",	"\033[23@"}},
  {VK_F12,	{"\033[24~",	"\033[24$",	"\033[24^",	"\033[24@"}},
  /* CTRL-6 complies with Windows cmd console but should be fixed */
  {'6',		{NULL,		NULL,		"\036",		NULL}},
  /* Table end marker */
  {0}
};

const char *
get_nonascii_key (INPUT_RECORD& input_rec, char *tmp)
{
#define NORMAL  0
#define SHIFT	1
#define CONTROL	2
/*#define CONTROLSHIFT	3*/

  int modifier_index = NORMAL;
  if (input_rec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED)
    modifier_index = SHIFT;
  if (input_rec.Event.KeyEvent.dwControlKeyState &
		(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
    modifier_index += CONTROL;

  for (int i = 0; keytable[i].vk; i++)
    if (input_rec.Event.KeyEvent.wVirtualKeyCode == keytable[i].vk)
      {
        if ((input_rec.Event.KeyEvent.dwControlKeyState &
		(LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
	    && keytable[i].val[modifier_index] != NULL)
          { /* Generic ESC prefixing if Alt is pressed */
	    tmp[0] = '\033';
	    strcpy (tmp + 1, keytable[i].val[modifier_index]);
	    return tmp;
          }
        else
          return keytable[i].val[modifier_index];
      }

  if (input_rec.Event.KeyEvent.uChar.AsciiChar)
    {
      tmp[0] = input_rec.Event.KeyEvent.uChar.AsciiChar;
      tmp[1] = '\0';
      return tmp;
    }
  return NULL;
}

int
fhandler_console::init (HANDLE f, DWORD a, mode_t bin)
{
  // this->fhandler_termios::init (f, mode, bin);
  /* Ensure both input and output console handles are open */
  int flags = 0;

  a &= GENERIC_READ | GENERIC_WRITE;
  if (a == GENERIC_READ)
    flags = O_RDONLY;
  if (a == GENERIC_WRITE)
    flags = O_WRONLY;
  if (a == (GENERIC_READ | GENERIC_WRITE))
    flags = O_RDWR;
  open (flags | O_BINARY);
  if (f != INVALID_HANDLE_VALUE)
    CloseHandle (f);	/* Reopened by open */

  return !tcsetattr (0, &tc->ti);
}

int
fhandler_console::igncr_enabled ()
{
  return tc->ti.c_iflag & IGNCR;
}

void
fhandler_console::set_close_on_exec (bool val)
{
  fhandler_base::set_close_on_exec (val);
  set_no_inheritance (output_handle, val);
}

void __stdcall
set_console_title (char *title)
{
  wchar_t buf[TITLESIZE + 1];
  sys_mbstowcs (buf, TITLESIZE + 1, title);
  lock_ttys here (15000);
  SetConsoleTitleW (buf);
  debug_printf ("title '%W'", buf);
}

void
fhandler_console::fixup_after_fork_exec (bool execing)
{
  HANDLE h = get_handle ();
  HANDLE oh = get_output_handle ();

  if ((execing && close_on_exec ()) || open (O_NOCTTY | get_flags (), 0))
    cygheap->manage_console_count ("fhandler_console::fixup_after_fork_exec", -1);
  else
    {
      if (!get_io_handle ())
	system_printf ("error opening input console handle for %s after fork/exec, errno %d, %E", get_name (), get_errno ());
      if (!get_output_handle ())
	system_printf ("error opening output console handle for %s after fork/exec, errno %d, %E", get_name (), get_errno ());
    }

  if (!close_on_exec ())
    {
      CloseHandle (h);
      CloseHandle (oh);
    }
}

bool NO_COPY fhandler_console::invisible_console;

// #define WINSTA_ACCESS (WINSTA_READATTRIBUTES | STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE | WINSTA_CREATEDESKTOP | WINSTA_EXITWINDOWS)
#define WINSTA_ACCESS WINSTA_ALL_ACCESS

/* Create a console in an invisible window station.  This should work
   in all versions of Windows NT except Windows 7 (so far). */
bool
fhandler_console::create_invisible_console (HWINSTA horig)
{
  HWINSTA h = CreateWindowStationW (NULL, 0, WINSTA_ACCESS, NULL);
  termios_printf ("%p = CreateWindowStation(NULL), %E", h);
  BOOL b;
  if (h)
    {
      b = SetProcessWindowStation (h);
      termios_printf ("SetProcessWindowStation %d, %E", b);
    }
  b = AllocConsole ();	/* will cause flashing if CreateWindowStation
	 		   failed */
  if (!h)
    SetParent (GetConsoleWindow (), HWND_MESSAGE);
  if (horig && h && h != horig && SetProcessWindowStation (horig))
    CloseWindowStation (h);
  termios_printf ("%d = AllocConsole (), %E", b);
  invisible_console = true;
  return b;
}

/* Ugly workaround for Windows 7.

   First try to just attach to any console which may have started this
   app.  If that works use this as our "invisible console".

   This will fail if not started from the command prompt.  In that case, start
   a dummy console application in a hidden state so that we can use its console
   as our invisible console.  This probably works everywhere but process
   creation is slow and to be avoided if possible so the window station method
   is vastly preferred.

   FIXME: This is not completely thread-safe since it creates two inheritable
   handles which are known only to this function.  If another thread starts
   a process the new process will inherit these handles.  However, since this
   function is currently only called at startup and during exec, it shouldn't
   be a big deal.  */
bool
fhandler_console::create_invisible_console_workaround ()
{
  if (!AttachConsole (-1))
    {
      bool taskbar;
      DWORD err = GetLastError ();
      path_conv helper ("/bin/cygwin-console-helper.exe");
      HANDLE hello = NULL;
      HANDLE goodbye = NULL;
      /* If err == ERROR_PROC_FOUND then this method won't work.  But that's
	 ok.  The window station method should work ok when AttachConsole doesn't
	 work.

	 If the helper doesn't exist or we can't create event handles then we
	 can't use this method. */
      if (err == ERROR_PROC_NOT_FOUND || !helper.exists ()
	  || !(hello = CreateEvent (&sec_none, true, false, NULL))
	  || !(goodbye = CreateEvent (&sec_none, true, false, NULL)))
	{
	  AllocConsole ();	/* This is just sanity check code.  We should
				   never actually hit here unless we're running
				   in an environment which lacks the helper
				   app. */
	  taskbar = true;
	}
      else
	{
	  STARTUPINFOW si = {};
	  PROCESS_INFORMATION pi;
	  size_t len = helper.get_wide_win32_path_len ();
	  WCHAR cmd[len + (2 * strlen (" 0xffffffff")) + 1];
	  WCHAR title[] = L"invisible cygwin console";

	  helper.get_wide_win32_path (cmd);
	  __small_swprintf (cmd + len, L" %p %p", hello, goodbye);

	  si.cb = sizeof (si);
	  si.dwFlags = STARTF_USESHOWWINDOW;
	  si.wShowWindow = SW_HIDE;
	  si.lpTitle = title;

	  /* Create a new hidden process.  Use the two event handles as
	     argv[1] and argv[2]. */
	  BOOL x = CreateProcessW (NULL, cmd, &sec_none_nih, &sec_none_nih, true,
				   CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
	  if (x)
	    {
	      CloseHandle (pi.hProcess);	/* Don't need */
	      CloseHandle (pi.hThread);		/*  these.    */
	    }
	  taskbar = false;
	  /* Wait for subprocess to indicate that it is live.  This may not
	     actually be needed but it's hard to say since it is possible that
	     there will be no console for a brief time after the process
	     returns and there is no easy way to determine if/when this happens
	     in Windows.  So play it safe. */
	  if (!x || (WaitForSingleObject (hello, 10000) != WAIT_OBJECT_0)
	      || !AttachConsole (pi.dwProcessId))
	    AllocConsole ();	/* Oh well.  Watch the flash. */
	}

      if (!taskbar)
	/* Setting the owner of the console window to HWND_MESSAGE seems to
	   hide it from the taskbar.  Don't know if this method is faster than
	   calling ShowWindowAsync but it should guarantee no taskbar presence
	   for the hidden console. */
	SetParent (GetConsoleWindow (), HWND_MESSAGE);
      if (hello)
	CloseHandle (hello);
      if (goodbye)
	{
	  SetEvent (goodbye);	/* Tell helper process it's ok to exit. */
	  CloseHandle (goodbye);
	}
    }
  return invisible_console = true;
}

bool
fhandler_console::need_invisible ()
{
  BOOL b = false;
  if (GetConsoleCP ())
    invisible_console = false;
  else
    {
      HWINSTA h;
      /* The intent here is to allocate an "invisible" console if we have no
	 controlling tty or to reuse the existing console if we already have
	 a tty.  So, first get the old window station.  If there is no controlling
	 terminal, create a new window station and then set it as the current
	 window station.  The subsequent AllocConsole will then be allocated
	 invisibly.  But, after doing that we have to restore any existing windows
	 station or, strangely, characters will not be displayed in any windows
	 drawn on the current screen.  We only do this if we have changed to
	 a new window station and if we had an existing windows station previously.
	 We also close the previously opened window station even though AllocConsole
	 is now "using" it.  This doesn't seem to cause any problems.

	 Things to watch out for if you make changes in this code:

	 - Flashing, black consoles showing up when you start, e.g., ssh in
	   an xterm.
	 - Non-displaying of characters in rxvt or xemacs if you start a
	   process using setsid: bash -lc "setsid rxvt".  */

      h = GetProcessWindowStation ();

      USEROBJECTFLAGS oi;
      DWORD len;
      if (!h
	  || !GetUserObjectInformationW (h, UOI_FLAGS, &oi, sizeof (oi), &len)
	  || !(oi.dwFlags & WSF_VISIBLE))
	{
	  b = true;
	  debug_printf ("window station is not visible");
	  AllocConsole ();
	  invisible_console = true;
	}
      else if (wincap.has_broken_alloc_console ())
	b = create_invisible_console_workaround ();
      else
	b = create_invisible_console (h);
    }

  debug_printf ("invisible_console %d", invisible_console);
  return b;
}
