static int
dialog_switch_pos (dialog_t diag, dialog_pos_t new_pos)
{
  if (new_pos != diag->pos)
    {
      switch (diag->pos)
	{
	case DIALOG_POS_OK:
	  move (diag->ok_y, diag->ok_x);
	  addstr (diag->ok);
	  break;
	case DIALOG_POS_CANCEL:
          if (diag->cancel)
            {
              move (diag->cancel_y, diag->cancel_x);
              addstr (diag->cancel);
            }
	  break;
	default:
	  break;
	}
      diag->pos = new_pos;
      switch (diag->pos)
	{
	case DIALOG_POS_PIN:
	  move (diag->pin_y, diag->pin_x + diag->pin_loc);
	  set_cursor_state (1);
	  break;
	case DIALOG_POS_OK:
	  set_cursor_state (0);
	  move (diag->ok_y, diag->ok_x);
	  standout ();
	  addstr (diag->ok);
	  standend ();
	  move (diag->ok_y, diag->ok_x);
	  break;
	case DIALOG_POS_CANCEL:
          if (diag->cancel)
            {
              set_cursor_state (0);
              move (diag->cancel_y, diag->cancel_x);
              standout ();
              addstr (diag->cancel);
              standend ();
              move (diag->cancel_y, diag->cancel_x);
            }
	  break;
	case DIALOG_POS_NONE:
	  set_cursor_state (0);
	  break;
	}
      refresh ();
    }
  return 0;
}
static int
dialog_run (pinentry_t pinentry, const char *tty_name, const char *tty_type)
{
  struct dialog diag;
  FILE *ttyfi = NULL;
  FILE *ttyfo = NULL;
  SCREEN *screen = 0;
  int done = 0;
  char *pin_utf8;
  int alt = 0;
#ifndef HAVE_DOSISH_SYSTEM
  int no_input = 1;
#endif
#ifdef HAVE_NCURSESW
  char *old_ctype = NULL;

  if (pinentry->lc_ctype)
    {
      old_ctype = strdup (setlocale (LC_CTYPE, NULL));
      setlocale (LC_CTYPE, pinentry->lc_ctype);
    }
  else
    setlocale (LC_CTYPE, "");
#endif

  /* Open the desired terminal if necessary.  */
  if (tty_name)
    {
      ttyfi = fopen (tty_name, "r");
      if (!ttyfi)
        {
          pinentry->specific_err = ASSUAN_ENOENT;
          return -1;
        }
      ttyfo = fopen (tty_name, "w");
      if (!ttyfo)
	{
	  int err = errno;
	  fclose (ttyfi);
	  errno = err;
          pinentry->specific_err = ASSUAN_ENOENT;
	  return -1;
	}
      screen = newterm (tty_type, ttyfo, ttyfi);
      set_term (screen);
    }
  else
    {
      if (!init_screen)
	{
          if (!(isatty(fileno(stdin)) && isatty(fileno(stdout))))
            {
              errno = ENOTTY;
              pinentry->specific_err = ASSUAN_ENOTTY;
              return -1;
            }
	  init_screen = 1;
	  initscr ();
	}
      else
	clear ();
    }

  keypad (stdscr, TRUE); /* Enable keyboard mapping.  */
  nonl ();		/* Tell curses not to do NL->CR/NL on output.  */
  cbreak ();		/* Take input chars one at a time, no wait for \n.  */
  noecho ();		/* Don't echo input - in color.  */

  if (has_colors ())
    {
      start_color ();
#ifdef NCURSES_VERSION
      use_default_colors ();
#endif

      if (pinentry->color_so == PINENTRY_COLOR_DEFAULT)
	{
	  pinentry->color_so = PINENTRY_COLOR_RED;
	  pinentry->color_so_bright = 1;
	}
      if (COLOR_PAIRS >= 2)
	{
	  init_pair (1, pinentry_color[pinentry->color_fg],
		     pinentry_color[pinentry->color_bg]);
	  init_pair (2, pinentry_color[pinentry->color_so],
		     pinentry_color[pinentry->color_bg]);

	  bkgd (COLOR_PAIR (1));
	  attron (COLOR_PAIR (1) | (pinentry->color_fg_bright ? A_BOLD : 0));
	}
    }
  refresh ();

  /* Create the dialog.  */
  if (dialog_create (pinentry, &diag))
    {
      /* Note: pinentry->specific_err has already been set.  */
      endwin ();
      if (screen)
        delscreen (screen);

#ifdef HAVE_NCURSESW
      if (old_ctype)
        {
          setlocale (LC_CTYPE, old_ctype);
          free (old_ctype);
        }
#endif
      if (ttyfi)
        fclose (ttyfi);
      if (ttyfo)
        fclose (ttyfo);
      return -2;
    }
  dialog_switch_pos (&diag,
		     diag.pinentry->pin ? DIALOG_POS_PIN : DIALOG_POS_OK);

#ifndef HAVE_DOSISH_SYSTEM
  wtimeout (stdscr, 70);
#endif

  do
    {
      int c;

      c = wgetch (stdscr);     /* Refresh, accept single keystroke of input.  */
#ifndef HAVE_DOSISH_SYSTEM
      if (timed_out && no_input)
	{
	  done = -2;
	  break;
	}
#endif

      switch (c)
	{
	case ERR:
#ifndef HAVE_DOSISH_SYSTEM
	  continue;
#else
          done = -2;
          break;
#endif

	case 27: /* Alt was pressed.  */
	  alt = 1;
	  /* Get the next key press.  */
	  continue;

	case KEY_LEFT:
	case KEY_UP:
	  switch (diag.pos)
	    {
	    case DIALOG_POS_OK:
	      if (diag.pinentry->pin)
		dialog_switch_pos (&diag, DIALOG_POS_PIN);
	      break;
	    case DIALOG_POS_NOTOK:
	      dialog_switch_pos (&diag, DIALOG_POS_OK);
	      break;
	    case DIALOG_POS_CANCEL:
	      if (diag.notok)
		dialog_switch_pos (&diag, DIALOG_POS_NOTOK);
	      else
		dialog_switch_pos (&diag, DIALOG_POS_OK);
	      break;
	    default:
	      break;
	    }
	  break;

	case KEY_RIGHT:
	case KEY_DOWN:
	  switch (diag.pos)
	    {
	    case DIALOG_POS_PIN:
	      dialog_switch_pos (&diag, DIALOG_POS_OK);
	      break;
	    case DIALOG_POS_OK:
	      if (diag.notok)
		dialog_switch_pos (&diag, DIALOG_POS_NOTOK);
	      else
		dialog_switch_pos (&diag, DIALOG_POS_CANCEL);
	      break;
	    case DIALOG_POS_NOTOK:
	      dialog_switch_pos (&diag, DIALOG_POS_CANCEL);
	      break;
	    default:
	      break;
	    }
	  break;

	case '\t':
	  switch (diag.pos)
	    {
	    case DIALOG_POS_PIN:
	      dialog_switch_pos (&diag, DIALOG_POS_OK);
	      break;
	    case DIALOG_POS_OK:
	      if (diag.notok)
		dialog_switch_pos (&diag, DIALOG_POS_NOTOK);
	      else
		dialog_switch_pos (&diag, DIALOG_POS_CANCEL);
	      break;
	    case DIALOG_POS_NOTOK:
	      dialog_switch_pos (&diag, DIALOG_POS_CANCEL);
	      break;
	    case DIALOG_POS_CANCEL:
	      if (diag.pinentry->pin)
		dialog_switch_pos (&diag, DIALOG_POS_PIN);
	      else
		dialog_switch_pos (&diag, DIALOG_POS_OK);
	      break;
	    default:
	      break;
	    }
	  break;

	case '\005':
	  done = -2;
	  break;

	case '\r':
	  switch (diag.pos)
	    {
	    case DIALOG_POS_PIN:
	    case DIALOG_POS_OK:
	      done = 1;
	      break;
	    case DIALOG_POS_NOTOK:
	      done = -1;
	      break;
	    case DIALOG_POS_CANCEL:
	      done = -2;
	      break;
            case DIALOG_POS_NONE:
              break;
	    }
	  break;

	default:
	  if (diag.pos == DIALOG_POS_PIN)
	    dialog_input (&diag, alt, c);
	}
#ifndef HAVE_DOSISH_SYSTEM
      no_input = 0;
#endif
      if (c != -1)
	alt = 0;
    }
  while (!done);

  if (diag.pinentry->pin)
    /* NUL terminate the passphrase.  dialog_run makes sure there is
       enough space for the terminating NUL byte.  */
    diag.pinentry->pin[diag.pin_len] = 0;

  set_cursor_state (1);
  endwin ();
  if (screen)
    delscreen (screen);

#ifdef HAVE_NCURSESW
  if (old_ctype)
    {
      setlocale (LC_CTYPE, old_ctype);
      free (old_ctype);
    }
#endif
  if (ttyfi)
    fclose (ttyfi);
  if (ttyfo)
    fclose (ttyfo);
  /* XXX Factor out into dialog_release or something.  */
  free (diag.ok);
  if (diag.cancel)
    free (diag.cancel);
  if (diag.notok)
    free (diag.notok);

  if (pinentry->pin)
    {
      pinentry->locale_err = 1;
      pin_utf8 = pinentry_local_to_utf8 (pinentry->lc_ctype, pinentry->pin, 1);
      if (pin_utf8)
	{
	  pinentry_setbufferlen (pinentry, strlen (pin_utf8) + 1);
	  if (pinentry->pin)
	    strcpy (pinentry->pin, pin_utf8);
	  secmem_free (pin_utf8);
	  pinentry->locale_err = 0;
	}
    }

  if (done == -2)
    pinentry->canceled = 1;

  if (diag.pinentry->pin)
    return done < 0 ? -1 : diag.pin_len;
  else
    return done < 0 ? 0 : 1;
}
static int
dialog_run (pinentry_t pinentry, const char *tty_name, const char *tty_type)
{
  struct dialog diag;
  FILE *ttyfi = NULL;
  FILE *ttyfo = NULL;
  SCREEN *screen = 0;
  int done = 0;
  char *pin_utf8;

  /* Open the desired terminal if necessary.  */
  if (tty_name)
    {
      ttyfi = fopen (tty_name, "r");
      if (!ttyfi)
	return -1;
      ttyfo = fopen (tty_name, "w");
      if (!ttyfo)
	{
	  int err = errno;
	  fclose (ttyfi);
	  errno = err;
	  return -1;
	}
      screen = newterm ((char *)tty_type, ttyfo, ttyfi);
      set_term (screen);
    }
  else
    {
      if (!init_screen)
	{
	  init_screen = 1;
	  initscr ();
	}
      else
	clear ();
    }
  
  keypad (stdscr, TRUE); /* Enable keyboard mapping.  */
  nonl ();		/* Tell curses not to do NL->CR/NL on output.  */
  cbreak ();		/* Take input chars one at a time, no wait for \n.  */
  noecho ();		/* Don't echo input - in color.  */

  if (has_colors ())
    {
      start_color ();
      use_default_colors ();

      if (pinentry->color_so == PINENTRY_COLOR_DEFAULT)
	{
	  pinentry->color_so = PINENTRY_COLOR_RED;
	  pinentry->color_so_bright = 1;
	}
      if (COLOR_PAIRS >= 2)
	{
	  init_pair (1, pinentry_color[pinentry->color_fg],
		     pinentry_color[pinentry->color_bg]);
	  init_pair (2, pinentry_color[pinentry->color_so],
		     pinentry_color[pinentry->color_bg]);

	  bkgd (COLOR_PAIR (1));
	  attron (COLOR_PAIR (1) | (pinentry->color_fg_bright ? A_BOLD : 0));
	}
    }
  refresh ();

  /* XXX */
  if (dialog_create (pinentry, &diag))
    return -2;
  dialog_switch_pos (&diag, diag.pin ? DIALOG_POS_PIN : DIALOG_POS_OK);

  do
    {
      int c;

      c = getch ();     /* Refresh, accept single keystroke of input.  */

      switch (c)
	{
	case KEY_LEFT:
	case KEY_UP:
	  switch (diag.pos)
	    {
	    case DIALOG_POS_OK:
	      if (diag.pin)
		dialog_switch_pos (&diag, DIALOG_POS_PIN);
	      break;
	    case DIALOG_POS_NOTOK:
	      dialog_switch_pos (&diag, DIALOG_POS_OK);
	      break;
	    case DIALOG_POS_CANCEL:
	      if (diag.notok)
		dialog_switch_pos (&diag, DIALOG_POS_NOTOK);
	      else
		dialog_switch_pos (&diag, DIALOG_POS_OK);
	      break;
	    default:
	      break;
	    }
	  break;

	case KEY_RIGHT:
	case KEY_DOWN:
	  switch (diag.pos)
	    {
	    case DIALOG_POS_PIN:
	      dialog_switch_pos (&diag, DIALOG_POS_OK);
	      break;
	    case DIALOG_POS_OK:
	      if (diag.notok)
		dialog_switch_pos (&diag, DIALOG_POS_NOTOK);
	      else
		dialog_switch_pos (&diag, DIALOG_POS_CANCEL);
	      break;
	    case DIALOG_POS_NOTOK:
	      dialog_switch_pos (&diag, DIALOG_POS_CANCEL);
	      break;
	    default:
	      break;
	    }
	  break;

	case '\t':
	  switch (diag.pos)
	    {
	    case DIALOG_POS_PIN:
	      dialog_switch_pos (&diag, DIALOG_POS_OK);
	      break;
	    case DIALOG_POS_OK:
	      if (diag.notok)
		dialog_switch_pos (&diag, DIALOG_POS_NOTOK);
	      else
		dialog_switch_pos (&diag, DIALOG_POS_CANCEL);	      
	      break;
	    case DIALOG_POS_NOTOK:
	      dialog_switch_pos (&diag, DIALOG_POS_CANCEL);
	      break;
	    case DIALOG_POS_CANCEL:
	      if (diag.pin)
		dialog_switch_pos (&diag, DIALOG_POS_PIN);
	      else
		dialog_switch_pos (&diag, DIALOG_POS_OK);
	      break;
	    default:
	      break;
	    }
	  break;
  
	case '\005':
	  done = -2;
	  break;

	case '\r':
	  switch (diag.pos)
	    {
	    case DIALOG_POS_PIN:
	    case DIALOG_POS_OK:
	      done = 1;
	      break;
	    case DIALOG_POS_NOTOK:
	      done = -1;
	      break;
	    case DIALOG_POS_CANCEL:
	      done = -2;
	      break;
            case DIALOG_POS_NONE:
              break;
	    }
	  break;

	default:
	  if (diag.pos == DIALOG_POS_PIN)
	    dialog_input (&diag, c);
	}
    }
  while (!done);

  set_cursor_state (1);
  endwin ();
  if (screen)
    delscreen (screen);

  if (ttyfi)
    fclose (ttyfi);
  if (ttyfo)
    fclose (ttyfo);
  /* XXX Factor out into dialog_release or something.  */
  free (diag.ok);
  if (diag.cancel)
    free (diag.cancel);
  if (diag.notok)
    free (diag.notok);

  if (pinentry->pin)
    {
      pinentry->locale_err = 1;
      pin_utf8 = pinentry_local_to_utf8 (pinentry->lc_ctype, pinentry->pin, 1);
      if (pin_utf8)
	{
	  pinentry_setbufferlen (pinentry, strlen (pin_utf8) + 1);
	  if (pinentry->pin)
	    strcpy (pinentry->pin, pin_utf8);
	  secmem_free (pin_utf8);
	  pinentry->locale_err = 0;
	}
    }

  if (done == -2)
    pinentry->canceled = 1;

  return diag.pin ? (done < 0 ? -1 : diag.pin_len) : (done < 0 ? 0 : 1);
}