/* TN5250
 * Copyright (C) 1997 Michael Madore
 * 
 * This file is part of TN5250.
 *
 * TN5250 is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1, or (at your option)
 * any later version.
 * 
 * TN5250 is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307 USA
 * 
 */

#include "tn5250-private.h"

static void tn5250_display_add_dbuffer (Tn5250Display * display,
					Tn5250DBuffer * dbuffer);
void tn5250_display_kf_macro (Tn5250Display * This, int Ch);
void tn5250_display_set_cursor_next_progression_field (Tn5250Display *
						       This, int nextfield);
void tn5250_display_set_cursor_prev_progression_field (Tn5250Display *
						       This,
						       int currentfield);
void tn5250_display_wordwrap_delete (Tn5250Display * This);
void tn5250_display_wordwrap_insert (Tn5250Display * This, unsigned char c,
				     int shiftcount);
void tn5250_display_wordwrap_addch (Tn5250Display * This, unsigned char c);


/****f* lib5250/tn5250_display_new
 * NAME
 *    tn5250_display_new
 * SYNOPSIS
 *    ret = tn5250_display_new ();
 * INPUTS
 *    None
 * DESCRIPTION
 *    DOCUMENT ME!!!
 *****/
Tn5250Display *
tn5250_display_new ()
{
  Tn5250Display *This;

  if ((This = tn5250_new (Tn5250Display, 1)) == NULL)
    {
      return NULL;
    }
  This->display_buffers = NULL;
  This->macro = NULL;
  This->terminal = NULL;
  This->config = NULL;
  This->indicators = 0;
  This->indicators_dirty = 0;
  This->pending_insert = 0;
  This->sign_key_hack = 1;
  This->session = NULL;
  This->key_queue_head = This->key_queue_tail = 0;
  This->saved_msg_line = NULL;
  This->msg_line = NULL;
  This->map = NULL;
  This->keystate = TN5250_KEYSTATE_UNLOCKED;
  This->keySRC = TN5250_KBDSRC_NONE;

  tn5250_display_add_dbuffer (This, tn5250_dbuffer_new (80, 24));
  return This;
}


/****f* lib5250/tn5250_display_destroy
 * NAME
 *    tn5250_display_destroy
 * SYNOPSIS
 *    tn5250_display_destroy (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    DOCUMENT ME!!!
 *****/
void
tn5250_display_destroy (Tn5250Display * This)
{
  Tn5250DBuffer *diter, *dnext;

  if ((diter = This->display_buffers) != NULL)
    {
      do
	{
	  dnext = diter->next;
	  tn5250_dbuffer_destroy (diter);
	  diter = dnext;
	}
      while (diter != This->display_buffers);
    }
  if (This->terminal != NULL)
    {
      tn5250_terminal_destroy (This->terminal);
    }
  if (This->saved_msg_line != NULL)
    {
      free (This->saved_msg_line);
    }
  if (This->msg_line != NULL)
    {
      free (This->msg_line);
    }
  if (This->config != NULL)
    {
      tn5250_config_unref (This->config);
    }
  free (This);
  return;
}


/****f* lib5250/tn5250_display_config
 * NAME
 *    tn5250_display_config
 * SYNOPSIS
 *    tn5250_display_config (This, config);
 * INPUTS
 *    Tn5250Display *      This       - 
 *    Tn5250Config *       config     -
 * DESCRIPTION
 *    Applies configuration to the display.
 *****/
int
tn5250_display_config (Tn5250Display * This, Tn5250Config * config)
{
  const char *v;
  const char *termtype;

  tn5250_config_ref (config);
  if (This->config != NULL)
    {
      tn5250_config_unref (This->config);
    }
  This->config = config;

  /* check if the +/- sign keyboard hack should be enabled */
  if (tn5250_config_get (config, "sign_key_hack"))
    {
      This->sign_key_hack = tn5250_config_get_bool (config, "sign_key_hack");
    }

  /* Set a terminal type if necessary */
  termtype = tn5250_config_get (config, "env.TERM");

  if (termtype == NULL)
    {
      tn5250_config_set (config, "env.TERM", "IBM-3179-2");
    }

  /* Set the new character map. */
  if (This->map != NULL)
    {
      tn5250_char_map_destroy (This->map);
    }
  if ((v = tn5250_config_get (config, "map")) == NULL)
    {
      tn5250_config_set (config, "map", "37");
      v = tn5250_config_get (config, "map");
    }

  This->map = tn5250_char_map_new (v);
  if (This->map == NULL)
    {
      return -1;		/* FIXME: An error message would be nice. */
    }

  return 0;
}


/****f* lib5250/tn5250_display_set_session
 * NAME
 *    tn5250_display_set_session
 * SYNOPSIS
 *    tn5250_display_set_session (This, s);
 * INPUTS
 *    Tn5250Display *      This       - 
 *    struct _Tn5250Session * s          - 
 * DESCRIPTION
 *    DOCUMENT ME!!!
 *****/
void
tn5250_display_set_session (Tn5250Display * This, struct _Tn5250Session *s)
{
  This->session = s;
  if (This->session != NULL)
    {
      This->session->display = This;
    }
  return;
}


/****f* lib5250/tn5250_display_push_dbuffer
 * NAME
 *    tn5250_display_push_dbuffer
 * SYNOPSIS
 *    ret = tn5250_display_push_dbuffer (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Create a new display buffer and assign the old one an id so we can
 *    later restore it.  Return the id which must be > 0.
 *****/
Tn5250DBuffer *
tn5250_display_push_dbuffer (Tn5250Display * This)
{
  Tn5250DBuffer *dbuf;

  dbuf = tn5250_dbuffer_copy (This->display_buffers);
  tn5250_display_add_dbuffer (This, dbuf);
  return dbuf;			/* Pointer is used as unique identifier in data stream. */
}


/****f* lib5250/tn5250_display_restore_dbuffer
 * NAME
 *    tn5250_display_restore_dbuffer
 * SYNOPSIS
 *    tn5250_display_restore_dbuffer (This, id);
 * INPUTS
 *    Tn5250Display *      This       - 
 *    Tn5250DBuffer *      id         - 
 * DESCRIPTION
 *    Delete the current dbuffer and replace it with the one with id `id'.
 *****/
void
tn5250_display_restore_dbuffer (Tn5250Display * This, Tn5250DBuffer * id)
{
  Tn5250DBuffer *iter;

  /* Sanity check to make sure that the display buffer is for real and
   * that it isn't the one which is currently active. */
  if ((iter = This->display_buffers) != NULL)
    {
      do
	{
	  if (iter == id && iter != This->display_buffers)
	    {
	      break;
	    }
	  iter = iter->next;
	}
      while (iter != This->display_buffers);

      if (iter != id || iter == This->display_buffers)
	{
	  return;
	}
    }
  else
    {
      return;
    }

  This->display_buffers->prev->next = This->display_buffers->next;
  This->display_buffers->next->prev = This->display_buffers->prev;
  tn5250_dbuffer_destroy (This->display_buffers);
  This->display_buffers = iter;
  return;
}


/****i* lib5250/tn5250_display_add_dbuffer
 * NAME
 *    tn5250_display_add_dbuffer
 * SYNOPSIS
 *    tn5250_display_add_dbuffer (This, dbuffer);
 * INPUTS
 *    Tn5250Display *      This       - 
 *    Tn5250DBuffer *      dbuffer    - 
 * DESCRIPTION
 *    Add a display buffer into this display's circularly linked list of
 *    display buffers.
 *****/
static void
tn5250_display_add_dbuffer (Tn5250Display * This, Tn5250DBuffer * dbuffer)
{
  TN5250_ASSERT (dbuffer != NULL);

  if (This->display_buffers == NULL)
    {
      This->display_buffers = dbuffer;
      dbuffer->next = dbuffer->prev = dbuffer;
    }
  else
    {
      dbuffer->next = This->display_buffers;
      dbuffer->prev = This->display_buffers->prev;
      dbuffer->next->prev = dbuffer;
      dbuffer->prev->next = dbuffer;
    }
  return;
}


/****f* lib5250/tn5250_display_set_terminal
 * NAME
 *    tn5250_display_set_terminal
 * SYNOPSIS
 *    tn5250_display_set_terminal (This, term);
 * INPUTS
 *    Tn5250Display *      This       - 
 *    Tn5250Terminal *     term       - 
 * DESCRIPTION
 *    Set the terminal associated with this display.
 *****/
void
tn5250_display_set_terminal (Tn5250Display * This, Tn5250Terminal * term)
{
  if (This->terminal != NULL)
    {
      tn5250_terminal_destroy (This->terminal);
    }
  This->terminal = term;
  This->indicators_dirty = 1;
  tn5250_terminal_update (This->terminal, This);
  tn5250_terminal_update_indicators (This->terminal, This);
  return;
}


/****f* lib5250/tn5250_display_update
 * NAME
 *    tn5250_display_update
 * SYNOPSIS
 *    tn5250_display_update (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Update the terminal's representation of the display.
 *****/
void
tn5250_display_update (Tn5250Display * This)
{
  if (This->msg_line != NULL)
    {
      int l;
      l = tn5250_dbuffer_msg_line (This->display_buffers);
      memcpy (This->display_buffers->data +
	      tn5250_display_width (This) * l, This->msg_line, This->msg_len);
    }
  if (This->terminal != NULL)
    {
      tn5250_terminal_update (This->terminal, This);
      if (This->indicators_dirty)
	{
	  tn5250_terminal_update_indicators (This->terminal, This);
	  This->indicators_dirty = 0;
	}
    }
  return;
}


/****f* lib5250/tn5250_display_waitevent
 * NAME
 *    tn5250_display_waitevent
 * SYNOPSIS
 *    ret = tn5250_display_waitevent (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Wait for a terminal event.  Handle keystrokes while we're at it
 *    and don't return those to the session (what would it do with them?)
 *****/
int
tn5250_display_waitevent (Tn5250Display * This)
{
  int is_x_system, r, handled_key = 0;

  if (This->terminal == NULL)
    {
      return 0;
    }

  while (1)
    {
      is_x_system = (This->keystate == TN5250_KEYSTATE_LOCKED);

      /* Handle keys from our key queue if we aren't X SYSTEM. */
      if (This->key_queue_head != This->key_queue_tail && !is_x_system)
	{
	  TN5250_LOG (("Handling buffered key.\n"));
	  tn5250_display_do_key (This, This->key_queue[This->key_queue_head]);
	  if (++This->key_queue_head == TN5250_DISPLAY_KEYQ_SIZE)
	    {
	      This->key_queue_head = 0;
	    }
	  handled_key = 1;
	  continue;
	}

      /* don't make the user press HELP to see what the error is */
      if (This->keystate == TN5250_KEYSTATE_PREHELP)
	{
	  tn5250_display_do_key (This, K_HELP);
	  handled_key = 1;
	}

      if (handled_key)
	{
	  tn5250_display_update (This);
	  handled_key = 0;
	}
      r = tn5250_terminal_waitevent (This->terminal);
      if ((r & TN5250_TERMINAL_EVENT_KEY) != 0)
	{
	  tn5250_display_do_keys (This);
	}

      if ((r & ~TN5250_TERMINAL_EVENT_KEY) != 0)
	{
	  return r;
	}
    }
}


/****f* lib5250/tn5250_display_getkey
 * NAME
 *    tn5250_display_getkey
 * SYNOPSIS
 *    ret = tn5250_display_getkey (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Get the next keystroke in the keyboard buffer.
 *****/
int
tn5250_display_getkey (Tn5250Display * This)
{
  if (This->terminal == NULL)
    {
      return -1;
    }
  return tn5250_terminal_getkey (This->terminal);
}


/****f* lib5250/tn5250_display_beep
 * NAME
 *    tn5250_display_beep
 * SYNOPSIS
 *    tn5250_display_beep (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    The required beep function.
 *****/
void
tn5250_display_beep (Tn5250Display * This)
{
  const char *cmd;
  int ec;

  if ((cmd = tn5250_config_get (This->config, "beep_command")) != NULL)
    {
      ec = system (cmd);
      if (ec == -1)
	{
	  TN5250_LOG (("system() for beep command failed: %s\n",
		       strerror (errno)));
	}
      else if (ec != 0)
	{
	  TN5250_LOG (("beep command exited with errno %d\n", ec));
	}
      return;
    }

  if (This->terminal == NULL)
    return;

  tn5250_terminal_beep (This->terminal);
  return;
}


/****f* lib5250/tn5250_display_set_pending_insert
 * NAME
 *    tn5250_display_set_pending_insert
 * SYNOPSIS
 *    tn5250_display_set_pending_insert (This, y, x);
 * INPUTS
 *    Tn5250Display *      This       - 
 *    int                  y          - 
 *    int                  x          - 
 * DESCRIPTION
 *    Set the pending insert flag and the insert cursor position.
 *****/
void
tn5250_display_set_pending_insert (Tn5250Display * This, int y, int x)
{
  This->pending_insert = 1;
  tn5250_dbuffer_set_ic (This->display_buffers, y, x);
  return;
}


/****f* lib5250/tn5250_display_field_at
 * NAME
 *    tn5250_display_field_at
 * SYNOPSIS
 *    ret = tn5250_display_field_at (This, y, x);
 * INPUTS
 *    Tn5250Display *      This       - 
 *    int                  y          - 
 *    int                  x          - 
 * DESCRIPTION
 *    DOCUMENT ME!!!
 *****/
Tn5250Field *
tn5250_display_field_at (Tn5250Display * This, int y, int x)
{
  return tn5250_dbuffer_field_yx (This->display_buffers, y, x);
}


/****f* lib5250/tn5250_display_current_field
 * NAME
 *    tn5250_display_current_field
 * SYNOPSIS
 *    ret = tn5250_display_current_field (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Find the field currently under the cursor.  The answer might be NULL
 *    if there is no field under the cursor.
 *****/
Tn5250Field *
tn5250_display_current_field (Tn5250Display * This)
{
  return tn5250_display_field_at (This,
				  tn5250_display_cursor_y (This),
				  tn5250_display_cursor_x (This));
}


/****f* lib5250/tn5250_display_next_field
 * NAME
 *    tn5250_display_next_field
 * SYNOPSIS
 *    ret = tn5250_display_next_field (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Return a pointer to the next field after the current one which is not
 *    a bypass field.
 *****/
Tn5250Field *
tn5250_display_next_field (Tn5250Display * This)
{
  Tn5250Field *iter = NULL, *next;
  int y, x;

  y = tn5250_display_cursor_y (This);
  x = tn5250_display_cursor_x (This);

  iter = tn5250_display_field_at (This, y, x);
  if (iter == NULL)
    {
      /* Find the first field on the display after the cursor, wrapping if we
       * hit the bottom of the display. */
      while (iter == NULL)
	{
	  if ((iter = tn5250_display_field_at (This, y, x)) == NULL)
	    {
	      if (++x == tn5250_dbuffer_width (This->display_buffers))
		{
		  x = 0;
		  if (++y == tn5250_dbuffer_height (This->display_buffers))
		    {
		      y = 0;
		    }
		}
	      if (y == tn5250_display_cursor_y (This) &&
		  x == tn5250_display_cursor_x (This))
		{
		  return NULL;	/* No fields on display */
		}
	    }
	}
    }
  else
    {
      iter = iter->next;
    }

  next = iter;
  while (tn5250_field_is_bypass (next))
    {
      next = next->next;	/* Hehe */
      if (next == iter && tn5250_field_is_bypass (next))
	{
	  return NULL;		/* No non-bypass fields. */
	}
    }

  return next;
}


/****f* lib5250/tn5250_display_prev_field
 * NAME
 *    tn5250_display_prev_field
 * SYNOPSIS
 *    ret = tn5250_display_prev_field (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Return a pointer to the first preceding field which is not a bypass
 *    field.
 *****/
Tn5250Field *
tn5250_display_prev_field (Tn5250Display * This)
{
  Tn5250Field *iter = NULL, *prev;
  int y, x;

  y = tn5250_display_cursor_y (This);
  x = tn5250_display_cursor_x (This);

  iter = tn5250_display_field_at (This, y, x);
  if (iter == NULL)
    {
      /* Find the first field on the display after the cursor, wrapping if we
       * hit the bottom of the display. */
      while (iter == NULL)
	{
	  if ((iter = tn5250_display_field_at (This, y, x)) == NULL)
	    {
	      if (x-- == 0)
		{
		  x = tn5250_dbuffer_width (This->display_buffers) - 1;
		  if (y-- == 0)
		    {
		      y = tn5250_dbuffer_height (This->display_buffers) - 1;
		    }
		}
	      if (y == tn5250_display_cursor_y (This) &&
		  x == tn5250_display_cursor_x (This))
		{
		  return NULL;	/* No fields on display */
		}
	    }
	}
    }
  else
    {
      iter = iter->prev;
    }

  prev = iter;
  while (tn5250_field_is_bypass (prev))
    {
      prev = prev->prev;	/* Hehe */
      if (prev == iter && tn5250_field_is_bypass (prev))
	{
	  return NULL;		/* No non-bypass fields. */
	}
    }

  return prev;
}


/****f* lib5250/tn5250_display_set_cursor_home
 * NAME
 *    tn5250_display_set_cursor_home
 * SYNOPSIS
 *    tn5250_display_set_cursor_home (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Set the cursor to the home position on the current display buffer.
 *    The home position is:
 *       1) the IC position, if we have one. 
 *       2) the first position of the first non-bypass field, if we have on.
 *       3) 0,0
 *****/
void
tn5250_display_set_cursor_home (Tn5250Display * This)
{
  if (This->pending_insert)
    {
      tn5250_dbuffer_goto_ic (This->display_buffers);
      /*     This->pending_insert = 0; */
    }
  else
    {
      int y = 0, x = 0;
      Tn5250Field *iter = This->display_buffers->field_list;
      if (iter != NULL)
	{
	  do
	    {
	      if (!tn5250_field_is_bypass (iter))
		{
		  y = tn5250_field_start_row (iter);
		  x = tn5250_field_start_col (iter);
		  break;
		}
	      iter = iter->next;
	    }
	  while (iter != This->display_buffers->field_list);
	}
      tn5250_display_set_cursor (This, y, x);
    }
  return;
}


/****f* lib5250/tn5250_display_set_cursor_field
 * NAME
 *    tn5250_display_set_cursor_field
 * SYNOPSIS
 *    tn5250_display_set_cursor_field (This, field);
 * INPUTS
 *    Tn5250Display *      This       - 
 *    Tn5250Field *        field      - 
 * DESCRIPTION
 *    Set the cursor position on the current display buffer to the home
 *    position of the specified field.
 *****/
void
tn5250_display_set_cursor_field (Tn5250Display * This, Tn5250Field * field)
{
  if (field == NULL)
    {
      tn5250_display_set_cursor_home (This);
      return;
    }
  tn5250_dbuffer_cursor_set (This->display_buffers,
			     tn5250_field_start_row (field),
			     tn5250_field_start_col (field));
  return;
}


/****f* lib5250/tn5250_display_set_cursor_next_field
 * NAME
 *    tn5250_display_set_cursor_next_field
 * SYNOPSIS
 *    tn5250_display_set_cursor_next_field (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Move the cursor to the next non-bypass field.  This will move the
 *    cursor to the home position if there are no non-bypass fields.
 *****/
void
tn5250_display_set_cursor_next_field (Tn5250Display * This)
{
  Tn5250Field *currentfield = tn5250_display_current_field (This);
  Tn5250Field *field;

  if ((currentfield != NULL) && (currentfield->nextfieldprogressionid != 0))
    {
      tn5250_display_set_cursor_next_progression_field (This,
							currentfield->
							nextfieldprogressionid);
    }
  else
    {
      field = tn5250_display_next_field (This);
      tn5250_display_set_cursor_field (This, field);
    }
  return;
}


/****f* lib5250/tn5250_display_set_cursor_next_progression_field
 * NAME
 *    tn5250_display_set_cursor_next_progression_field
 * SYNOPSIS
 *    tn5250_display_set_cursor_next_progression_field (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Move the cursor to the next specified field.  This will move the
 *    cursor to the next non-bypass field if no field is specified.
 *****/
void
tn5250_display_set_cursor_next_progression_field (Tn5250Display * This,
						  int nextfield)
{
  Tn5250Field *field;

  if (nextfield == 0)
    {
      tn5250_display_set_cursor_next_field (This);
      return;
    }

  while ((field = tn5250_display_next_field (This)) != NULL)
    {
      tn5250_display_set_cursor_field (This, field);

      if (field->entry_id == nextfield)
	{
	  break;
	}
    }
  return;
}


/****f* lib5250/tn5250_display_set_cursor_next_logical_field
 * NAME
 *    tn5250_display_set_cursor_next_logical_field
 * SYNOPSIS
 *    tn5250_display_set_cursor_next_logical_field (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Move the cursor to the next non-bypass field not of the same 
 *    continuous field set.  This will move the cursor to the home
 *    position if there are no non-bypass fields.
 *****/
void
tn5250_display_set_cursor_next_logical_field (Tn5250Display * This)
{
  Tn5250Field *currentfield = tn5250_display_current_field (This);
  int currentid, origid;

  if (currentfield != NULL)
    {
      currentid = currentfield->entry_id;
      origid = currentfield->id;

      while (currentid == currentfield->entry_id)
	{
	  tn5250_display_set_cursor_next_field (This);
	  currentfield = tn5250_display_current_field (This);

	  if ((currentfield == NULL) || (origid == currentfield->id))
	    {
	      break;
	    }
	}
    }
  else
    {
      tn5250_display_set_cursor_next_field (This);
    }
  return;
}


/****f* lib5250/tn5250_display_set_cursor_prev_field
 * NAME
 *    tn5250_display_set_cursor_prev_field
 * SYNOPSIS
 *    tn5250_display_set_cursor_prev_field (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Move the cursor to the previous non-bypass field.  This will move the
 *    cursor to the home position if there are no non-bypass fields.
 *****/
void
tn5250_display_set_cursor_prev_field (Tn5250Display * This)
{
  Tn5250Field *currentfield = tn5250_display_current_field (This);
  Tn5250Field *field;

  if ((currentfield != NULL) && (currentfield->entry_id != 0))
    {
      tn5250_display_set_cursor_prev_progression_field (This,
							currentfield->
							entry_id);
    }
  else
    {
      field = tn5250_display_prev_field (This);
      tn5250_display_set_cursor_field (This, field);
    }
  return;
}


/****f* lib5250/tn5250_display_set_cursor_prev_progression_field
 * NAME
 *    tn5250_display_set_cursor_prev_progression_field
 * SYNOPSIS
 *    tn5250_display_set_cursor_prev_progression_field (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Move the cursor to the previous progression field.
 *
 *****/
void
tn5250_display_set_cursor_prev_progression_field (Tn5250Display * This,
						  int currentfield)
{
  Tn5250Field *field;
  Tn5250Field *origfield;
  int orig_id;
  int differentfieldfound;

  if (currentfield == 0)
    {
      return;
    }

  origfield = tn5250_display_current_field (This);
  orig_id = origfield->id;
  differentfieldfound = 0;

  while ((field = tn5250_display_prev_field (This)) != NULL)
    {
      tn5250_display_set_cursor_field (This, field);

      if (field->entry_id == currentfield)
	{
	  if (field->id == orig_id)
	    {
	      field = tn5250_display_prev_field (This);
	      tn5250_display_set_cursor_field (This, field);
	      break;
	    }
	  if (!differentfieldfound)
	    {
	      break;
	    }
	}
      else
	{
	  differentfieldfound = 1;
	}

      if (field->nextfieldprogressionid == currentfield)
	{
	  break;
	}
    }
  return;
}


/****f* lib5250/tn5250_display_set_cursor_prev_logical_field
 * NAME
 *    tn5250_display_set_cursor_prev_logical_field
 * SYNOPSIS
 *    tn5250_display_set_cursor_prev_logical_field (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Move the cursor to the previous non-bypass field not of the same
 *    continuous field set.  This will move the cursor to the home
 *    position if there are no non-bypass fields.
 *****/
void
tn5250_display_set_cursor_prev_logical_field (Tn5250Display * This)
{
  Tn5250Field *currentfield;
  int currentid, origid;

  tn5250_display_set_cursor_prev_field (This);
  currentfield = tn5250_display_current_field (This);

  if (currentfield == NULL)
    {
      return;
    }

  currentid = currentfield->entry_id;
  origid = currentfield->id;

  while (currentid == currentfield->entry_id)
    {
      tn5250_display_set_cursor_prev_field (This);
      currentfield = tn5250_display_current_field (This);

      if ((currentfield == NULL) || (origid == currentfield->id))
	{
	  break;
	}
    }
  tn5250_display_set_cursor_next_field (This);
  return;
}


/*
 *    Reconstruct WTD data as it might be sent from a host.  We use this
 *    to save our format table and display buffer.  We assume the buffer
 *    has been initialized, and we append to it.
 */
void
tn5250_display_make_wtd_data (Tn5250Display * This, Tn5250Buffer * buf,
			      Tn5250DBuffer * src_dbuffer)
{
  Tn5250WTDContext *ctx;

  if ((ctx = tn5250_wtd_context_new (buf, src_dbuffer,
				     This->display_buffers)) == NULL)
    {
      return;
    }

  /* 
     These coordinates will be used in an IC order when the screen is 
     restored
   */
  tn5250_wtd_context_set_ic (ctx,
			     tn5250_display_cursor_y (This) + 1,
			     tn5250_display_cursor_x (This) + 1);

  tn5250_wtd_context_convert (ctx);
  tn5250_wtd_context_destroy (ctx);
  return;
}


/****f* lib5250/tn5250_display_interactive_addch
 * NAME
 *    tn5250_display_interactive_addch
 * SYNOPSIS
 *    tn5250_display_interactive_addch (This, ch);
 * INPUTS
 *    Tn5250Display *      This       - 
 *    unsigned char        ch         - 
 * DESCRIPTION
 *    Add a character to the display, and set the field's MDT flag.  This
 *    is meant to be called by the keyboard handler when the user is
 *    entering data.
 *****/
void
tn5250_display_interactive_addch (Tn5250Display * This, unsigned char ch)
{
  Tn5250Field *field = tn5250_display_current_field (This);
  Tn5250Field *contfield;
  int end_of_field = 0;
  int nextfieldprogressionid = 0;

  if (field == NULL || tn5250_field_is_bypass (field))
    {
      This->keystate = TN5250_KEYSTATE_PREHELP;
      This->keySRC = TN5250_KBDSRC_PROTECT;
      tn5250_display_inhibit (This);
      return;
    }
  /* Upcase the character if this is a monocase field. */
  if (tn5250_field_is_monocase (field) && isalpha (ch))
    {
      ch = toupper (ch);
    }

  /* '+' and '-' keys activate field exit/field minus for numeric fields. */
  if (This->sign_key_hack && (tn5250_field_is_num_only (field)
			      || tn5250_field_is_signed_num (field)))
    {
      switch (ch)
	{
	case '+':
	  tn5250_display_kf_field_plus (This);
	  return;

	case '-':
	  tn5250_display_kf_field_minus (This);
	  return;
	}
    }

  /* Make sure this is a valid data character for this field type. */
  if (!tn5250_field_valid_char (field, ch, &(This->keySRC)))
    {
      TN5250_LOG (("Inhibiting: invalid character for field type.\n"));
      This->keystate = TN5250_KEYSTATE_PREHELP;
      tn5250_display_inhibit (This);
      return;
    }
  /* Are we at the last character of the field? */
  if (tn5250_display_cursor_y (This) == tn5250_field_end_row (field) &&
      tn5250_display_cursor_x (This) == tn5250_field_end_col (field))
    {
      end_of_field = 1;

      if (field->nextfieldprogressionid != 0)
	{
	  nextfieldprogressionid = field->nextfieldprogressionid;
	}
    }

  /* Don't allow the user to enter data in the sign portion of a signed
   * number field. */
  if (end_of_field && tn5250_field_is_signed_num (field))
    {
      TN5250_LOG (("Inhibiting: last character of signed num field.\n"));
      This->keystate = TN5250_KEYSTATE_PREHELP;
      This->keySRC = TN5250_KBDSRC_SIGNPOS;
      tn5250_display_inhibit (This);
      return;
    }

  /* Add or insert the character (depending on whether insert mode is on). */
  if ((tn5250_display_indicators (This) & TN5250_DISPLAY_IND_INSERT) != 0)
    {
      int ofs = tn5250_field_length (field) - 1;
      unsigned char *data = tn5250_display_field_data (This, field);

      if (tn5250_field_is_continued (field))
	{
	  contfield = field;
	  while (!tn5250_field_is_continued_last (contfield))
	    {
	      contfield = contfield->next;
	    }
	  ofs = tn5250_field_length (contfield) - 1;
	  data = tn5250_display_field_data (This, contfield);
	}

      if (tn5250_field_is_signed_num (field))
	{
	  ofs--;
	}

      if ((data[ofs] != '\0')
	  && (tn5250_char_map_to_local (This->map, data[ofs]) != ' ')
	  && (data[ofs] != TN5250_DISPLAY_WORD_WRAP_SPACE))
	{
	  This->keystate = TN5250_KEYSTATE_PREHELP;
	  This->keySRC = TN5250_KBDSRC_NOROOM;
	  tn5250_display_inhibit (This);
	  return;
	}

      if (tn5250_field_is_wordwrap (field))
	{
	  tn5250_display_wordwrap_insert (This,
					  tn5250_char_map_to_remote (This->
								     map, ch),
					  tn5250_field_count_right (field,
								    tn5250_display_cursor_y
								    (This),
								    tn5250_display_cursor_x
								    (This)));
	}
      else
	{
	  tn5250_dbuffer_ins (This->display_buffers, field->id,
			      tn5250_char_map_to_remote (This->map, ch),
			      tn5250_field_count_right (field,
							tn5250_display_cursor_y
							(This),
							tn5250_display_cursor_x
							(This)));
	}
    }
  else
    {
      if (tn5250_field_is_wordwrap (field)
	  || (tn5250_field_is_continued_last (field)
	      && tn5250_field_is_wordwrap (field->prev)))
	{
	  tn5250_display_wordwrap_addch (This,
					 tn5250_char_map_to_remote (This->map,
								    ch));
	}
      else
	{
	  if (This->terminal->putkey != NULL)
	    {
	      tn5250_terminal_putkey (This->terminal, This, ch,
				      tn5250_display_cursor_y (This),
				      tn5250_display_cursor_x (This));
	    }

	  tn5250_dbuffer_addch (This->display_buffers,
				tn5250_char_map_to_remote (This->map, ch));
	}
    }

  tn5250_field_set_mdt (field);

  /* If at the end of the field and not a fer field and not a word wrap
   * field advance to the next field.
   */
  if (end_of_field && !tn5250_field_is_wordwrap (field))
    {
      if (tn5250_field_is_fer (field))
	{
	  tn5250_display_indicator_set (This, TN5250_DISPLAY_IND_FER);
	  tn5250_display_set_cursor (This,
				     tn5250_field_end_row (field),
				     tn5250_field_end_col (field));
	}
      else
	{
	  tn5250_display_field_adjust (This, field);
	  if (tn5250_field_is_auto_enter (field))
	    {
	      tn5250_display_do_aidkey (This, TN5250_SESSION_AID_ENTER);
	      return;
	    }
	  if (nextfieldprogressionid != 0)
	    {
	      tn5250_display_set_cursor_next_progression_field (This,
								nextfieldprogressionid);
	    }
	  else
	    {
	      /* If we are at the end of the field tn5250_dbuffer_addch()
	       * above may have moved the cursor beyond the end of the
	       * field.  That screws us up in the case of continuous fields
	       * because each individual field does not have a progression
	       * ID set.  Since continuous fields may not be the next
	       * field to the right we need to be sure that the cursor
	       * is currently in the field, not to the right of it.  Doing
	       * so will ensure that the following call puts us in the
	       * next continuous field.  This has the side effect that
	       * inserting into a single character field does not move
	       * the cursor to the next field.  Maybe that's a bug, maybe
	       * that's a feature  :)
	       */
	      tn5250_dbuffer_left (This->display_buffers);
	      tn5250_display_set_cursor_next_field (This);
	    }
	}
    }
  return;
}


/****f* lib5250/tn5250_display_shift_right
 * NAME
 *    tn5250_display_shift_right
 * SYNOPSIS
 *    tn5250_display_shift_right (This, field, fill);
 * INPUTS
 *    Tn5250Display *      This       - 
 *    Tn5250Field *        field      - 
 *    unsigned char        fill       - 
 * DESCRIPTION
 *    Move all the data characters in the field to the right-hand side of
 *    the field and left-fill the field with `fill' characters.
 *****/
void
tn5250_display_shift_right (Tn5250Display * This, Tn5250Field * field,
			    unsigned char fill)
{
  int n, end;
  unsigned char *ptr;

  ptr = tn5250_display_field_data (This, field);
  end = tn5250_field_length (field) - 1;

  tn5250_field_set_mdt (field);

  /* Don't adjust the sign position of signed num type fields. */
  if (tn5250_field_is_signed_num (field))
    {
      end--;
    }

  /* Left fill the field until the first non-null or non-blank character. */
  for (n = 0; n <= end && (ptr[n] == 0 || ptr[n] == 0x40); n++)
    {
      ptr[n] = fill;
    }

  /* If the field is entirely blank and we don't do this, we spin forever. */
  if (n > end)
    {
      return;
    }

  /* Shift the contents of the field right one place and put the fill char in
   * position 0 until the data is right-justified in the field. */
  while (ptr[end] == 0 || ptr[end] == 0x40)
    {
      for (n = end; n > 0; n--)
	{
	  ptr[n] = ptr[n - 1];
	}
      ptr[0] = fill;
    }
  return;
}


/****f* lib5250/tn5250_display_field_adjust
 * NAME
 *    tn5250_display_field_adjust
 * SYNOPSIS
 *    tn5250_display_field_adjust (This, field);
 * INPUTS
 *    Tn5250Display *      This       - 
 *    Tn5250Field *        field      - 
 * DESCRIPTION
 *    Adjust the field data as required by the Field Format Word.  This is
 *    called from tn5250_display_kf_field_exit.
 *****/
void
tn5250_display_field_adjust (Tn5250Display * This, Tn5250Field * field)
{
  int mand_fill_type;

  /* Because of special processing during transmit and other weirdness,
   * we need to shift signed number fields right regardless. */
  mand_fill_type = tn5250_field_mand_fill_type (field);
  if (tn5250_field_type (field) == TN5250_FIELD_SIGNED_NUM)
    {
      mand_fill_type = TN5250_FIELD_RIGHT_BLANK;
    }

  switch (mand_fill_type)
    {
    case TN5250_FIELD_NO_ADJUST:
    case TN5250_FIELD_MANDATORY_FILL:
      break;
    case TN5250_FIELD_RIGHT_ZERO:
      tn5250_display_shift_right (This, field,
				  tn5250_char_map_to_remote (This->map, '0'));
      break;
    case TN5250_FIELD_RIGHT_BLANK:
      tn5250_display_shift_right (This, field,
				  tn5250_char_map_to_remote (This->map, ' '));
      break;
    }

  tn5250_field_set_mdt (field);
  return;
}


/****f* lib5250/tn5250_display_do_keys
 * NAME
 *    tn5250_display_do_keys
 * SYNOPSIS
 *    tn5250_display_do_keys (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Handle keys from the terminal until we run out or are in the X SYSTEM
 *    state.
 *****/
void
tn5250_display_do_keys (Tn5250Display * This)
{
  int cur_key;
  char Last;
  int dokey;

  TN5250_LOG (("display_do_keys!\n"));

  do
    {
      cur_key = tn5250_macro_getkey (This, &Last);

      if (Last)
	{
	  tn5250_display_indicator_clear (This, TN5250_DISPLAY_IND_MACRO);
	}

      if (cur_key == 0)
	{
	  cur_key = tn5250_display_getkey (This);
	}

      if (cur_key != -1)
	{
	  tn5250_macro_reckey (This, cur_key);
	  dokey = 0;

	  switch (This->keystate)
	    {
	    case TN5250_KEYSTATE_UNLOCKED:
	      dokey = 1;
	      break;
	    case TN5250_KEYSTATE_HARDWARE:
	      if (cur_key == K_RESET)
		{
		  TN5250_LOG (("doing key %d in hw error state.\n", cur_key));
		}
	      dokey = 1;
	      break;
	    case TN5250_KEYSTATE_LOCKED:
	      switch (cur_key)
		{
		case K_SYSREQ:
		case K_ATTENTION:
		  TN5250_LOG (("doing key %d in locked state.\n", cur_key));
		  dokey = 1;
		  break;
		}
	      break;
	    case TN5250_KEYSTATE_PREHELP:
	      switch (cur_key)
		{
		case K_RESET:
		case K_HELP:
		case K_ATTENTION:
		  dokey = 1;
		  TN5250_LOG (("Doing key %d in prehelp state\n", cur_key));
		  break;
		}
	      break;
	      break;
	    case TN5250_KEYSTATE_POSTHELP:
	      switch (cur_key)
		{
		case K_RESET:
		case K_ATTENTION:
		  TN5250_LOG (("Doing key %d in posthelp state.\n", cur_key));
		  dokey = 1;
		  break;
		}
	    }

	  if (!dokey)
	    {
	      if ((This->key_queue_tail + 1 == This->key_queue_head) ||
		  (This->key_queue_head == 0 &&
		   This->key_queue_tail == TN5250_DISPLAY_KEYQ_SIZE - 1))
		{
		  TN5250_LOG (("Beep: Key queue full.\n"));
		  tn5250_display_beep (This);
		}
	      This->key_queue[This->key_queue_tail] = cur_key;
	      if (++This->key_queue_tail == TN5250_DISPLAY_KEYQ_SIZE)
		{
		  This->key_queue_tail = 0;
		}
	    }
	  else
	    {
	      /* if we're hitting a special keypress (such as error reset)
	         in a state where typeahead is not allowed, then clear
	         the key queue */
	      if (This->key_queue_head != This->key_queue_tail)
		{
		  This->key_queue_head = This->key_queue_tail = 0;
		}
	      tn5250_display_do_key (This, cur_key);
	    }
	}
    }
  while (cur_key != -1);

  tn5250_display_update (This);
  return;
}


/****f* lib5250/tn5250_display_do_key
 * NAME
 *    tn5250_display_do_key
 * SYNOPSIS
 *    tn5250_display_do_key (This, key);
 * INPUTS
 *    Tn5250Display *      This       - 
 *    int                  key        - 
 * DESCRIPTION
 *    Translate a keystroke and handle it.
 *****/
void
tn5250_display_do_key (Tn5250Display * This, int key)
{
  int pre_FER_clear = 0;

  TN5250_LOG (("@key %d\n", key));

  /* FIXME: Translate from terminal key via keyboard map to 5250 key. */

  switch (This->keystate)
    {
    case TN5250_KEYSTATE_UNLOCKED:
      TN5250_ASSERT (!tn5250_display_inhibited (This));
      /* normal processing can continue */
      break;
    case TN5250_KEYSTATE_HARDWARE:
      if (key != K_RESET)
	{
	  TN5250_LOG (("Denying key %d in hw err state.\n", key));
	  tn5250_display_beep (This);
	  return;
	}
      break;
    case TN5250_KEYSTATE_LOCKED:
      if (key != K_SYSREQ && key != K_PRINT && key != K_ATTENTION)
	{
	  TN5250_LOG (("Denying key %d in locked state.\n", key));
	  tn5250_display_beep (This);
	  return;
	}
      break;
    case TN5250_KEYSTATE_PREHELP:
      if (key != K_RESET && key != K_HELP
	  && key != K_PRINT && key != K_ATTENTION)
	{
	  TN5250_LOG (("Denying key %d in prehelp state.\n", key));
	  tn5250_display_beep (This);
	  return;
	}
      break;
    case TN5250_KEYSTATE_POSTHELP:
      if (key != K_RESET && key != K_ATTENTION)
	{
	  TN5250_LOG (("Denying key %d in posthelp state.\n", key));
	  tn5250_display_beep (This);
	  return;
	}
      break;
    }


  /* In the case we are in the field exit required state, we inhibit on
   * everything except left arrow, backspace, field exit, field+, and
   * field- */
  if ((tn5250_display_indicators (This) & TN5250_DISPLAY_IND_FER) != 0)
    {
      switch (key)
	{
	case K_LEFT:
	case K_BACKSPACE:
	  tn5250_display_indicator_clear (This, TN5250_DISPLAY_IND_FER);
	  return;

	case K_UP:
	case K_DOWN:
	case K_RIGHT:
	  tn5250_display_indicator_clear (This, TN5250_DISPLAY_IND_FER);
	  break;

	case K_ENTER:
	case K_FIELDEXIT:
	case K_FIELDMINUS:
	case K_FIELDPLUS:
	case K_TAB:
	case K_BACKTAB:
	case K_RESET:
	  pre_FER_clear = 1;
	  break;

	default:
	  This->keystate = TN5250_KEYSTATE_PREHELP;
	  This->keySRC = TN5250_KBDSRC_FER;
	  tn5250_display_inhibit (This);
	  TN5250_LOG (("Denying key %d in FER state.\n", key));
	  return;
	}
    }

  switch (key)
    {
    case K_RESET:
      tn5250_display_uninhibit (This);
      This->keystate = TN5250_KEYSTATE_UNLOCKED;
      break;

    case K_BACKSPACE:
      tn5250_display_kf_backspace (This);
      break;

    case K_LEFT:
      tn5250_display_kf_left (This);
      break;

    case K_RIGHT:
      tn5250_display_kf_right (This);
      break;

    case K_UP:
      tn5250_display_kf_up (This);
      break;

    case K_DOWN:
      tn5250_display_kf_down (This);
      break;

    case K_HELP:
      tn5250_display_do_aidkey (This, TN5250_SESSION_AID_HELP);
      break;

    case K_HOME:
      tn5250_display_kf_home (This);
      break;

    case K_END:
      tn5250_display_kf_end (This);
      break;

    case K_DELETE:
      tn5250_display_kf_delete (This);
      break;

    case K_INSERT:
      tn5250_display_kf_insert (This);
      break;

    case K_TAB:
      tn5250_display_kf_tab (This);
      break;

    case K_BACKTAB:
      tn5250_display_kf_backtab (This);
      break;

    case K_ENTER:
      tn5250_display_do_aidkey (This, TN5250_SESSION_AID_ENTER);
      break;

    case K_ROLLDN:
      tn5250_display_do_aidkey (This, TN5250_SESSION_AID_PGUP);
      break;

    case K_ROLLUP:
      tn5250_display_do_aidkey (This, TN5250_SESSION_AID_PGDN);
      break;

    case K_FIELDEXIT:
      tn5250_display_kf_field_exit (This);
      break;

    case K_FIELDPLUS:
      tn5250_display_kf_field_plus (This);
      break;

    case K_FIELDMINUS:
      tn5250_display_kf_field_minus (This);
      break;

    case K_TESTREQ:
      tn5250_display_do_aidkey (This, TN5250_SESSION_AID_TESTREQ);
      break;

    case K_SYSREQ:
      tn5250_display_do_aidkey (This, TN5250_SESSION_AID_SYSREQ);
      break;

    case K_ATTENTION:
      tn5250_display_uninhibit (This);
      This->keystate = TN5250_KEYSTATE_UNLOCKED;
      tn5250_display_do_aidkey (This, TN5250_SESSION_AID_ATTN);
      break;

    case K_PRINT:
      tn5250_display_do_aidkey (This, TN5250_SESSION_AID_PRINT);
      break;

    case K_DUPLICATE:
      tn5250_display_kf_dup (This);
      break;

    case K_NEXTWORD:
      tn5250_display_kf_nextword (This);
      break;

    case K_PREVWORD:
      tn5250_display_kf_prevword (This);
      break;

    case K_NEXTFLD:
      tn5250_display_kf_nextfld (This);
      break;

    case K_PREVFLD:
      tn5250_display_kf_prevfld (This);
      break;

    case K_FIELDHOME:
      tn5250_display_kf_fieldhome (This);
      break;

    case K_NEWLINE:
      tn5250_display_kf_newline (This);
      break;

    case K_MEMO:
      tn5250_display_kf_macro (This, K_MEMO);
      break;

    case K_EXEC:
      tn5250_display_kf_macro (This, K_EXEC);
      break;

    default:
      /* Handle function/command keys. */
      if (key >= K_F1 && key <= K_F24)
	{
	  if ((!tn5250_macro_recfunct (This, key)) &&
	      (!tn5250_macro_execfunct (This, key)))
	    {
	      if (key <= K_F12)
		{
		  TN5250_LOG (("Key = %d; K_F1 = %d; Key - K_F1 = %d\n",
			       key, K_F1, key - K_F1));
		  TN5250_LOG (("AID_F1 = %d; Result = %d\n",
			       TN5250_SESSION_AID_F1,
			       key - K_F1 + TN5250_SESSION_AID_F1));
		  tn5250_display_do_aidkey (This,
					    key - K_F1 +
					    TN5250_SESSION_AID_F1);
		}
	      else
		{
		  tn5250_display_do_aidkey (This,
					    key - K_F13 +
					    TN5250_SESSION_AID_F13);
		}
	    }
	  break;
	}

      /* Handle data keys. */
      if (key >= ' ' && key <= 255)
	{
	  TN5250_LOG (("HandleKey: key = %c\n", key));
	  tn5250_display_interactive_addch (This, key);
	}
      else
	{
	  TN5250_LOG (("HandleKey: Weird key ignored: %d\n", key));
	}
    }
  if (pre_FER_clear)
    {
      tn5250_display_indicator_clear (This, TN5250_DISPLAY_IND_FER);
    }
  return;
}


/****f* lib5250/tn5250_display_field_pad_and_adjust
 * NAME
 *    tn5250_display_field_pad_and_adjust
 * SYNOPSIS
 *    tn5250_display_field_pad_and_adjust(This, field);
 * INPUTS
 *    Tn5250Display *      This       - 
 *    Tn5250Display *      field      -
 * DESCRIPTION
 *    This nulls out the remainder of the field (after the
 *    cursor position) except for the +/- sign, and then right
 *    adjusts the field.  Does NOT do auto-enter, and does NOT
 *    advance to the next field.
 *****/
void
tn5250_display_field_pad_and_adjust (Tn5250Display * This,
				     Tn5250Field * field)
{
  Tn5250Field *iter;
  unsigned char *data;
  int i, l;

  /* NULL out remainder of field from cursor position.  For signed numeric
   * fields, we do *not* want to null out the sign position - Field+ and
   * Field- will do this for us.  We do not do this when the FER indicator
   * is set.
   */
  if ((tn5250_display_indicators (This) & TN5250_DISPLAY_IND_FER) == 0)
    {
      data = tn5250_display_field_data (This, field);
      i = tn5250_field_count_left (field, tn5250_display_cursor_y (This),
				   tn5250_display_cursor_x (This));
      l = tn5250_field_length (field);

      if (tn5250_field_is_signed_num (field))
	{
	  l--;
	}
      for (; i < l; i++)
	{
	  data[i] = 0;
	}

      if (tn5250_field_is_continued (field)
	  && (!tn5250_field_is_continued_last (field)))
	{
	  iter = field->next;

	  while (tn5250_field_is_continued (iter))
	    {
	      data = tn5250_display_field_data (This, iter);
	      l = tn5250_field_length (iter);

	      for (i = 0; i < l; i++)
		{
		  data[i] = 0;
		}

	      if (tn5250_field_is_continued_last (iter))
		{
		  break;
		}
	      iter = iter->next;
	    }
	}
    }

  tn5250_display_field_adjust (This, field);
  return;
}


/****f* lib5250/tn5250_display_kf_field_exit
 * NAME
 *    tn5250_display_kf_field_exit
 * SYNOPSIS
 *    tn5250_display_kf_field_exit (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Process a field exit function.
 *****/
void
tn5250_display_kf_field_exit (Tn5250Display * This)
{
  Tn5250Field *field;

  field = tn5250_display_current_field (This);

  if (field == NULL || tn5250_field_is_bypass (field))
    {
      This->keystate = TN5250_KEYSTATE_PREHELP;
      This->keySRC = TN5250_KBDSRC_PROTECT;
      tn5250_display_inhibit (This);
      return;
    }

  tn5250_display_field_pad_and_adjust (This, field);

  if (tn5250_field_is_auto_enter (field))
    {
      tn5250_display_do_aidkey (This, TN5250_SESSION_AID_ENTER);
      return;
    }

  tn5250_display_set_cursor_next_logical_field (This);
  return;
}


/****f* lib5250/tn5250_display_kf_field_plus
 * NAME
 *    tn5250_display_kf_field_plus
 * SYNOPSIS
 *    tn5250_display_kf_field_plus (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Process a field plus function.
 *****/
void
tn5250_display_kf_field_plus (Tn5250Display * This)
{
  Tn5250Field *field;
  unsigned char *data;

  TN5250_LOG (("Field+ entered.\n"));

  field = tn5250_display_current_field (This);

  if (field == NULL || tn5250_field_is_bypass (field))
    {
      This->keystate = TN5250_KEYSTATE_PREHELP;
      This->keySRC = TN5250_KBDSRC_PROTECT;
      tn5250_display_inhibit (This);
      return;
    }

  tn5250_display_field_pad_and_adjust (This, field);

  /* NOTE: Field+ should act like field exit on a non-numeric field. */
  if ((field != NULL) &&
      ((tn5250_field_type (field) == TN5250_FIELD_SIGNED_NUM) ||
       (tn5250_field_type (field) == TN5250_FIELD_NUM_ONLY)))
    {

      /* We don't do anything for number only fields.  For signed numeric
       * fields, we change the sign position to a '+'. */
      data = tn5250_display_field_data (This, field);

      if (tn5250_field_type (field) != TN5250_FIELD_NUM_ONLY)
	{
	  data[tn5250_field_length (field) - 1] = 0;
	}
    }

  if (tn5250_field_is_auto_enter (field))
    {
      tn5250_display_do_aidkey (This, TN5250_SESSION_AID_ENTER);
      return;
    }

  tn5250_display_set_cursor_next_logical_field (This);
  return;
}


/****f* lib5250/tn5250_display_kf_field_minus
 * NAME
 *    tn5250_display_kf_field_minus
 * SYNOPSIS
 *    tn5250_display_kf_field_minus (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Process a field minus function.
 *****/
void
tn5250_display_kf_field_minus (Tn5250Display * This)
{
  Tn5250Field *field;
  unsigned char *data;

  TN5250_LOG (("Field- entered.\n"));

  field = tn5250_display_current_field (This);

  if ((field == NULL) ||
      ((tn5250_field_type (field) != TN5250_FIELD_SIGNED_NUM) &&
       (tn5250_field_type (field) != TN5250_FIELD_NUM_ONLY)))
    {
      This->keystate = TN5250_KEYSTATE_PREHELP;
      This->keySRC = TN5250_KBDSRC_FLDM_DISALLOWED;
      tn5250_display_inhibit (This);
      return;
    }

  tn5250_display_field_pad_and_adjust (This, field);

  /* For numeric only fields, we shift the data one character to the
   * left and change the zone to negative.  i.e. 0xf0 becomes 0xd0,
   * 0xf1 becomes 0xd1, etc.  in the rightmost position.  For
   * signed numeric fields, we change the sign position to a '-'. */
  data = tn5250_display_field_data (This, field);

  if (tn5250_field_type (field) == TN5250_FIELD_NUM_ONLY)
    {
      int i = tn5250_field_length (field) - 1;
      data[i] = (data[i] & 0x0F) | 0xd0;
    }
  else
    {
      data[tn5250_field_length (field) - 1] =
	tn5250_char_map_to_remote (This->map, '-');
    }

  if (tn5250_field_is_auto_enter (field))
    {
      tn5250_display_do_aidkey (This, TN5250_SESSION_AID_ENTER);
      return;
    }

  tn5250_display_set_cursor_next_logical_field (This);
  return;
}


/****f* lib5250/tn5250_display_do_aidkey
 * NAME
 *    tn5250_display_do_aidkey
 * SYNOPSIS
 *    tn5250_display_do_aidkey (This, aidcode);
 * INPUTS
 *    Tn5250Display *      This       - 
 *    int                  aidcode    - 
 * DESCRIPTION
 *    Handle an aid code.
 *****/
void
tn5250_display_do_aidkey (Tn5250Display * This, int aidcode)
{
  TN5250_LOG (("tn5250_display_do_aidkey (0x%02X) called.\n", aidcode));

  /* Aidcodes less than zero are pseudo-aid-codes (see session.h) */
  if (This->session->read_opcode || aidcode < 0)
    {
      /* FIXME: If this returns zero, we need to stop processing. */
      (*(This->session->handle_aidkey)) (This->session, aidcode);
    }
  return;
}


/****f* lib5250/tn5250_display_kf_dup
 * NAME
 *    tn5250_display_kf_dup
 * SYNOPSIS
 *    tn5250_display_kf_dup (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Process a DUP key function.
 *****/
void
tn5250_display_kf_dup (Tn5250Display * This)
{
  int i;
  Tn5250Field *field;
  unsigned char *data;

  field = tn5250_display_current_field (This);
  if (field == NULL || tn5250_field_is_bypass (field))
    {
      This->keystate = TN5250_KEYSTATE_PREHELP;
      This->keySRC = TN5250_KBDSRC_PROTECT;
      tn5250_display_inhibit (This);
      return;
    }

  tn5250_field_set_mdt (field);

  if (!tn5250_field_is_dup_enable (field))
    {
      This->keystate = TN5250_KEYSTATE_PREHELP;
      This->keySRC = TN5250_KBDSRC_DUP_DISALLOWED;
      tn5250_display_inhibit (This);
      return;
    }

  i = tn5250_field_count_left (field, tn5250_display_cursor_y (This),
			       tn5250_display_cursor_x (This));
  data = tn5250_display_field_data (This, field);
  for (; i < tn5250_field_length (field); i++)
    {
      data[i] = 0x1c;
    }

  if (tn5250_field_is_fer (field))
    {
      tn5250_display_indicator_set (This, TN5250_DISPLAY_IND_FER);
      tn5250_dbuffer_cursor_set (This->display_buffers,
				 tn5250_field_end_row (field),
				 tn5250_field_end_col (field));
    }
  else
    {
      tn5250_display_field_adjust (This, field);
      if (tn5250_field_is_auto_enter (field))
	{
	  tn5250_display_do_aidkey (This, TN5250_SESSION_AID_ENTER);
	  return;
	}
      tn5250_display_set_cursor_next_field (This);
    }
  return;
}


/****f* lib5250/tn5250_display_indicator_set
 * NAME
 *    tn5250_display_indicator_set
 * SYNOPSIS
 *    tn5250_display_indicator_set (This, inds);
 * INPUTS
 *    Tn5250Display *      This       - 
 *    int                  inds       - 
 * DESCRIPTION
 *    Set the specified indicators and set a flag noting that the indicators
 *    must be refreshed.
 *****/
void
tn5250_display_indicator_set (Tn5250Display * This, int inds)
{
  This->indicators |= inds;
  This->indicators_dirty = 1;
  return;
}


/****f* lib5250/tn5250_display_indicator_clear
 * NAME
 *    tn5250_display_indicator_clear
 * SYNOPSIS
 *    tn5250_display_indicator_clear (This, inds);
 * INPUTS
 *    Tn5250Display *      This       - 
 *    int                  inds       - 
 * DESCRIPTION
 *    Clear the specified indicators and set a flag noting that the indicators
 *    must be refreshed.
 *****/
void
tn5250_display_indicator_clear (Tn5250Display * This, int inds)
{
  This->indicators &= ~inds;
  This->indicators_dirty = 1;

  /* Restore the message line if we are clearing the X II indicator */
  if ((inds & TN5250_DISPLAY_IND_INHIBIT) != 0
      && This->saved_msg_line != NULL)
    {
      int l = tn5250_dbuffer_msg_line (This->display_buffers);
      memcpy (This->display_buffers->data +
	      l * tn5250_display_width (This), This->saved_msg_line,
	      tn5250_display_width (This));
      free (This->saved_msg_line);
      This->saved_msg_line = NULL;
      free (This->msg_line);
      This->msg_line = NULL;
    }
  return;
}


/****f* lib5250/tn5250_display_clear_unit
 * NAME
 *    tn5250_display_clear_unit
 * SYNOPSIS
 *    tn5250_display_clear_unit (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Clear the display and set the display size to 24x80.
 *****/
void
tn5250_display_clear_unit (Tn5250Display * This)
{
  tn5250_dbuffer_set_size (This->display_buffers, 24, 80);
  tn5250_display_indicator_set (This, TN5250_DISPLAY_IND_X_SYSTEM);
  This->keystate = TN5250_KEYSTATE_LOCKED;
  tn5250_display_indicator_clear (This,
				  TN5250_DISPLAY_IND_INSERT |
				  TN5250_DISPLAY_IND_INHIBIT |
				  TN5250_DISPLAY_IND_FER);
  This->pending_insert = 0;
  tn5250_dbuffer_set_ic (This->display_buffers, 0, 0);
  if (This->saved_msg_line != NULL)
    {
      free (This->saved_msg_line);
      This->saved_msg_line = NULL;
    }
  if (This->msg_line != NULL)
    {
      free (This->msg_line);
      This->msg_line = NULL;
    }
  return;
}


/****f* lib5250/tn5250_display_clear_unit_alternate
 * NAME
 *    tn5250_display_clear_unit_alternate
 * SYNOPSIS
 *    tn5250_display_clear_unit_alternate (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Clear the display and set the display size to 27x132.
 *****/
void
tn5250_display_clear_unit_alternate (Tn5250Display * This)
{
  tn5250_dbuffer_set_size (This->display_buffers, 27, 132);
  tn5250_display_indicator_set (This, TN5250_DISPLAY_IND_X_SYSTEM);
  This->keystate = TN5250_KEYSTATE_LOCKED;
  tn5250_display_indicator_clear (This,
				  TN5250_DISPLAY_IND_INSERT |
				  TN5250_DISPLAY_IND_INHIBIT |
				  TN5250_DISPLAY_IND_FER);
  This->pending_insert = 0;
  tn5250_dbuffer_set_ic (This->display_buffers, 0, 0);
  if (This->saved_msg_line != NULL)
    {
      free (This->saved_msg_line);
      This->saved_msg_line = NULL;
    }
  if (This->msg_line != NULL)
    {
      free (This->msg_line);
      This->msg_line = NULL;
    }
  return;
}


/****f* lib5250/tn5250_display_clear_format_table
 * NAME
 *    tn5250_display_clear_format_table
 * SYNOPSIS
 *    tn5250_display_clear_format_table (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Clear the format table.
 *****/
void
tn5250_display_clear_format_table (Tn5250Display * This)
{
  tn5250_dbuffer_clear_table (This->display_buffers);
  tn5250_display_set_cursor (This, 0, 0);
  tn5250_display_indicator_set (This, TN5250_DISPLAY_IND_X_SYSTEM);
  This->keystate = TN5250_KEYSTATE_LOCKED;
  tn5250_display_indicator_clear (This, TN5250_DISPLAY_IND_INSERT);
  return;
}


/****f* lib5250/tn5250_display_kf_backspace
 * NAME
 *    tn5250_display_kf_backspace
 * SYNOPSIS
 *    tn5250_display_kf_backspace (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Move the cursor left one position unless on the first position of
 *    field, in which case we move to the last position of the previous
 *    field. (Or inhibit if we aren't on a field).
 *****/
void
tn5250_display_kf_backspace (Tn5250Display * This)
{
  Tn5250Field *field = tn5250_display_current_field (This);
  if (field == NULL)
    {
      This->keystate = TN5250_KEYSTATE_PREHELP;
      This->keySRC = TN5250_KBDSRC_PROTECT;
      tn5250_display_inhibit (This);
      return;
    }

  /* If in first position of field, set cursor position to last position
   * of previous field. */
  if (tn5250_display_cursor_x (This) == tn5250_field_start_col (field) &&
      tn5250_display_cursor_y (This) == tn5250_field_start_row (field))
    {
      field = tn5250_display_prev_field (This);
      if (field == NULL)
	{
	  return;		/* Should never happen */
	}
      tn5250_display_set_cursor_field (This, field);
      if (tn5250_field_length (field) - 1 > 0)
	{
	  tn5250_dbuffer_right (This->display_buffers,
				tn5250_field_length (field) - 1);
	}
      return;
    }

  tn5250_dbuffer_left (This->display_buffers);
  return;
}


/****f* lib5250/tn5250_display_kf_left
 * NAME
 *    tn5250_display_kf_left
 * SYNOPSIS
 *    tn5250_display_kf_left (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Move the cursor left.
 *****/
void
tn5250_display_kf_left (Tn5250Display * This)
{
  tn5250_dbuffer_left (This->display_buffers);
  return;
}


/****f* lib5250/tn5250_display_kf_right
 * NAME
 *    tn5250_display_kf_right
 * SYNOPSIS
 *    tn5250_display_kf_right (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Move the cursor right.
 *****/
void
tn5250_display_kf_right (Tn5250Display * This)
{
  tn5250_dbuffer_right (This->display_buffers, 1);
  return;
}


/****f* lib5250/tn5250_display_kf_up
 * NAME
 *    tn5250_display_kf_up
 * SYNOPSIS
 *    tn5250_display_kf_up (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Move the cursor up.
 *****/
void
tn5250_display_kf_up (Tn5250Display * This)
{
  tn5250_dbuffer_up (This->display_buffers);
  return;
}


/****f* lib5250/tn5250_display_kf_down
 * NAME
 *    tn5250_display_kf_down
 * SYNOPSIS
 *    tn5250_display_kf_down (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Move the cursor down.
 *****/
void
tn5250_display_kf_down (Tn5250Display * This)
{
  tn5250_dbuffer_down (This->display_buffers);
  return;
}


/****f* lib5250/tn5250_display_kf_insert
 * NAME
 *    tn5250_display_kf_insert
 * SYNOPSIS
 *    tn5250_display_kf_insert (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Toggle the insert indicator.
 *****/
void
tn5250_display_kf_insert (Tn5250Display * This)
{
  if ((tn5250_display_indicators (This) & TN5250_DISPLAY_IND_INSERT) != 0)
    {
      tn5250_display_indicator_clear (This, TN5250_DISPLAY_IND_INSERT);
    }
  else
    {
      tn5250_display_indicator_set (This, TN5250_DISPLAY_IND_INSERT);
    }
  return;
}


/****f* lib5250/tn5250_display_kf_tab
 * NAME
 *    tn5250_display_kf_tab
 * SYNOPSIS
 *    tn5250_display_kf_tab (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Tab function.
 *****/
void
tn5250_display_kf_tab (Tn5250Display * This)
{
  tn5250_display_set_cursor_next_logical_field (This);
  return;
}


/****f* lib5250/tn5250_display_kf_backtab
 * NAME
 *    tn5250_display_kf_backtab
 * SYNOPSIS
 *    tn5250_display_kf_backtab (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Backwards tab function.
 *****/
void
tn5250_display_kf_backtab (Tn5250Display * This)
{
  /* Backtab: Move to start of this field, or start of previous field if
   * already there.
   */
  Tn5250Field *field = tn5250_display_current_field (This);

  if (field == NULL || tn5250_field_count_left (field,
						tn5250_display_cursor_y
						(This),
						tn5250_display_cursor_x
						(This)) == 0)
    {
      tn5250_display_set_cursor_prev_logical_field (This);
      return;
    }

  if (field != NULL)
    {
      tn5250_display_set_cursor_field (This, field);
    }
  else
    {
      tn5250_display_set_cursor_home (This);
    }
  return;
}


/****f* lib5250/tn5250_display_kf_end
 * NAME
 *    tn5250_display_kf_end
 * SYNOPSIS
 *    tn5250_display_kf_end (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    End key function.
 *****/
void
tn5250_display_kf_end (Tn5250Display * This)
{
  Tn5250Field *field = tn5250_display_current_field (This);
  if (field != NULL && !tn5250_field_is_bypass (field))
    {
      unsigned char *data = tn5250_display_field_data (This, field);
      int i = tn5250_field_length (field) - 1;
      int y = tn5250_field_start_row (field);
      int x = tn5250_field_start_col (field);

      if (data[i] == '\0')
	{
	  while (i > 0 && data[i] == '\0')
	    {
	      i--;
	    }
	  while (i >= 0)
	    {
	      if (++x == tn5250_display_width (This))
		{
		  x = 0;
		  if (++y == tn5250_display_height (This))
		    {
		      y = 0;
		    }
		}
	      i--;
	    }
	}
      else
	{
	  y = tn5250_field_end_row (field);
	  x = tn5250_field_end_col (field);
	}
      tn5250_display_set_cursor (This, y, x);
    }
  else
    {
      This->keystate = TN5250_KEYSTATE_PREHELP;
      This->keySRC = TN5250_KBDSRC_PROTECT;
      tn5250_display_inhibit (This);
    }
  return;
}


/****f* lib5250/tn5250_display_kf_home
 * NAME
 *    tn5250_display_kf_home
 * SYNOPSIS
 *    tn5250_display_kf_home (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Home key function.
 *****/
void
tn5250_display_kf_home (Tn5250Display * This)
{
  Tn5250Field *field;
  int gx, gy;

  if (This->pending_insert)
    {
      gy = This->display_buffers->tcy;
      gx = This->display_buffers->tcx;
    }
  else
    {
      field = tn5250_dbuffer_first_non_bypass (This->display_buffers);
      if (field != NULL)
	{
	  gy = tn5250_field_start_row (field);
	  gx = tn5250_field_start_col (field);
	}
      else
	{
	  gx = gy = 0;
	}
    }

  if (gy == tn5250_display_cursor_y (This)
      && gx == tn5250_display_cursor_x (This))
    {
      tn5250_display_do_aidkey (This, TN5250_SESSION_AID_RECORD_BS);
    }
  else
    {
      tn5250_display_set_cursor (This, gy, gx);
    }
  return;
}


/****f* lib5250/tn5250_display_kf_delete
 * NAME
 *    tn5250_display_kf_delete
 * SYNOPSIS
 *    tn5250_display_kf_delete (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Delete key function.
 *****/
void
tn5250_display_kf_delete (Tn5250Display * This)
{
  Tn5250Field *field = tn5250_display_current_field (This);

  if (field == NULL || tn5250_field_is_bypass (field))
    {
      This->keystate = TN5250_KEYSTATE_PREHELP;
      This->keySRC = TN5250_KBDSRC_PROTECT;
      tn5250_display_inhibit (This);
    }
  else
    {
      tn5250_field_set_mdt (field);

      /* If this field is word wrap handle it differently */
      if (tn5250_field_is_wordwrap (field))
	{
	  tn5250_display_wordwrap_delete (This);
	  return;
	}

      tn5250_dbuffer_del (This->display_buffers, field->id,
			  tn5250_field_count_right (field,
						    tn5250_display_cursor_y
						    (This),
						    tn5250_display_cursor_x
						    (This)));
    }
  return;
}


/****f* lib5250/tn5250_display_kf_prevword
 * NAME
 *    tn5250_display_kf_prevword
 * SYNOPSIS
 *    tn5250_display_kf_prevword (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Move cursor to the last non-blank character.
 *****/
void
tn5250_display_kf_prevword (Tn5250Display * This)
{
  tn5250_dbuffer_prevword (This->display_buffers);
  return;
}


/****f* lib5250/tn5250_display_kf_nextword
 * NAME
 *    tn5250_display_kf_nextword
 * SYNOPSIS
 *    tn5250_display_kf_nextword (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Move cursor to the next non-blank character.
 *****/
void
tn5250_display_kf_nextword (Tn5250Display * This)
{
  tn5250_dbuffer_nextword (This->display_buffers);
  return;
}


/****f* lib5250/tn5250_display_kf_prevfld
 * NAME
 *    tn5250_display_kf_prevfld
 * SYNOPSIS
 *    tn5250_display_kf_prevfld (This);
 * INPUTS
 *    Tn5250Display *      This       -
 * DESCRIPTION
 *    Move the cursor backward to the beginning of the previous non-blank
 *    or input field.  The intent is to emulate the dbuffer_prevword function
 *    and add the additional functionality of stopping at a (possibly blank)
 *    input-capable field as well.
 *****/
void
tn5250_display_kf_prevfld (Tn5250Display * This)
{
  int state = 0;
  int maxiter;
  Tn5250Field *field;

  TN5250_LOG (("dbuffer_prevfld: entered.\n"));

  maxiter = (This->display_buffers->w * This->display_buffers->h);
  TN5250_ASSERT (maxiter > 0);

  while (--maxiter)
    {
      tn5250_dbuffer_left (This->display_buffers);

      /* If at the start of a field, exit */
      field = tn5250_display_current_field (This);
      if ((field != NULL) &&
	  (tn5250_field_start_row (field) == This->display_buffers->cy) &&
	  (tn5250_field_start_col (field) == This->display_buffers->cx))
	{
	  break;
	}

      switch (state)
	{
	case 0:
	  if (This->display_buffers->data[This->display_buffers->cy *
					  This->display_buffers->w +
					  This->display_buffers->cx] <= 0x40)
	    {
	      state++;
	    }
	  break;
	case 1:
	  if (This->display_buffers->data[This->display_buffers->cy *
					  This->display_buffers->w +
					  This->display_buffers->cx] > 0x40)
	    {
	      state++;
	    }
	  break;
	case 2:
	  if (This->display_buffers->data[This->display_buffers->cy *
					  This->display_buffers->w +
					  This->display_buffers->cx] <= 0x40)
	    {
	      tn5250_dbuffer_right (This->display_buffers, 1);
	      return;
	    }
	  break;
	}
    }
  return;
}


/****f* lib5250/tn5250_display_kf_nextfld
 * NAME
 *    tn5250_display_kf_nextfld
 * SYNOPSIS
 *    tn5250_display_kf_nextfld (This);
 * INPUTS
 *    Tn5250Display *      This       -
 * DESCRIPTION
 *    Move the cursor forward to the beginning of the next non-blank
 *    or input field.  The intent is to emulate the dbuffer_nextword function
 *    and add the additional functionality of stopping at a (possibly blank)
 *    input-capable field as well.
 *****/
void
tn5250_display_kf_nextfld (Tn5250Display * This)
{
  int foundblank = 0;
  int maxiter;
  Tn5250Field *field;

  TN5250_LOG (("dbuffer_nextfld: entered.\n"));

  maxiter = (This->display_buffers->w * This->display_buffers->h);
  TN5250_ASSERT (maxiter > 0);

  while (--maxiter)
    {
      tn5250_dbuffer_right (This->display_buffers, 1);
      if (This->display_buffers->data[This->display_buffers->cy *
				      This->display_buffers->w +
				      This->display_buffers->cx] <= 0x40)
	{
	  foundblank++;
	}

      /* If found a blank previously and a non-blank now, exit */
      if ((foundblank)
	  && (This->display_buffers->
	      data[This->display_buffers->cy * This->display_buffers->w +
		   This->display_buffers->cx] > 0x40))
	{
	  break;
	}

      /* If at the start of a field, exit */
      field = tn5250_display_current_field (This);
      if ((field != NULL) &&
	  (tn5250_field_start_row (field) == This->display_buffers->cy) &&
	  (tn5250_field_start_col (field) == This->display_buffers->cx))
	{
	  break;
	}
    }
  return;
}


/****f* lib5250/tn5250_display_kf_fieldhome
 * NAME
 *    tn5250_display_kf_fieldhome
 * SYNOPSIS
 *    tn5250_display_kf_fieldhome (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Move cursor to the next non-blank character.
 *****/
void
tn5250_display_kf_fieldhome (Tn5250Display * This)
{
  Tn5250Field *field = tn5250_display_current_field (This);
  if (field != NULL && !tn5250_field_is_bypass (field))
    {
      int y = tn5250_field_start_row (field);
      int x = tn5250_field_start_col (field);
      tn5250_display_set_cursor (This, y, x);
    }
  else
    {
      This->keystate = TN5250_KEYSTATE_PREHELP;
      This->keySRC = TN5250_KBDSRC_PROTECT;
      tn5250_display_inhibit (This);
    }
  return;
}


/****f* lib5250/tn5250_display_kf_newline
 * NAME
 *    tn5250_display_kf_newline
 * SYNOPSIS
 *    tn5250_display_kf_newline (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Move cursor to the first field on the next line.
 *****/
void
tn5250_display_kf_newline (Tn5250Display * This)
{
  int y;
  Tn5250Field *f;

  y = tn5250_display_cursor_y (This);
  tn5250_display_set_cursor (This, y, 0);
  tn5250_dbuffer_down (This->display_buffers);
  f = tn5250_display_current_field (This);
  if (f == NULL || tn5250_field_is_bypass (f))
    {
      tn5250_display_set_cursor_next_field (This);
    }
  return;
}


/****f* lib5250/tn5250_display_kf_macro
 * NAME
 *    tn5250_display_kf_macro
 * SYNOPSIS
 *    tn5250_display_kf_macro (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 *    int		   Ch	    - choice : K_MEMO | K_EXEC
 * DESCRIPTION
 *    Toggle the memo/exec indicator.
 *****/
void
tn5250_display_kf_macro (Tn5250Display * This, int Ch)
{
  TN5250_LOG (("K_MEMO/EXEC\n"));

  if ((Ch == K_MEMO) && (!tn5250_macro_estate (This)))
    {
      if (!tn5250_macro_rstate (This))
	{
	  if (tn5250_macro_startdef (This))
	    {
	      tn5250_display_indicator_set (This, TN5250_DISPLAY_IND_MACRO);
	    }
	}
      else
	{
	  tn5250_macro_enddef (This);
	  tn5250_display_indicator_clear (This, TN5250_DISPLAY_IND_MACRO);
	}
    }

  if ((Ch == K_EXEC) && (!tn5250_macro_rstate (This)))
    {
      if (!tn5250_macro_estate (This))
	{
	  if (tn5250_macro_startexec (This))
	    {
	      tn5250_display_indicator_set (This, TN5250_DISPLAY_IND_MACRO);
	    }
	}
      else
	{
	  tn5250_macro_endexec (This);
	  tn5250_display_indicator_clear (This, TN5250_DISPLAY_IND_MACRO);
	}
    }
  return;
}


/****f* lib5250/tn5250_display_save_msg_line
 * NAME
 *    tn5250_display_save_msg_line
 * SYNOPSIS
 *    tn5250_display_save_msg_line (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Save the current message line.
 *****/
void
tn5250_display_save_msg_line (Tn5250Display * This)
{
  int l;

  if (This->saved_msg_line != NULL)
    {
      free (This->saved_msg_line);
    }
  This->saved_msg_line =
    (unsigned char *) malloc (tn5250_display_width (This));

  l = tn5250_dbuffer_msg_line (This->display_buffers);
  memcpy (This->saved_msg_line, This->display_buffers->data +
	  tn5250_display_width (This) * l, tn5250_display_width (This));
  return;
}


/****f* lib5250/tn5250_display_set_msg_line
 * NAME
 *    tn5250_display_set_msg_line
 * SYNOPSIS
 *    tn5250_display_set_msg_line (This);
 * INPUTS
 *    Tn5250Display *      This       -
 *    const unsigned char *msgline    -
 *    int                  msglen     -
 * DESCRIPTION
 *    Set a new message line.
 *****/
void
tn5250_display_set_msg_line (Tn5250Display * This,
			     const unsigned char *msgline, int msglen)
{
  int l;

  if (This->msg_line != NULL)
    {
      free (This->msg_line);
    }
  This->msg_line = (unsigned char *) malloc (tn5250_display_width (This));
  memset (This->msg_line, 0x00, tn5250_display_width (This));

  memcpy (This->msg_line, msgline, msglen);
  This->msg_len = msglen;

  l = tn5250_dbuffer_msg_line (This->display_buffers);
  memcpy (This->display_buffers->data + tn5250_display_width (This) * l,
	  This->msg_line, This->msg_len);
  return;
}


/****f* lib5250/tn5250_display_set_char_map
 * NAME
 *    tn5250_display_set_char_map
 * SYNOPSIS
 *    tn5250_display_set_char_map (display, "37");
 * INPUTS
 *    Tn5250Display *      display    - Pointer to the display object.
 *    const char *         name       - Name of translation map to use.
 * DESCRIPTION
 *    Save the current message line.
 *****/
void
tn5250_display_set_char_map (Tn5250Display * This, const char *name)
{
  Tn5250CharMap *map = tn5250_char_map_new (name);
  TN5250_ASSERT (map != NULL);
  if (This->map != NULL)
    {
      tn5250_char_map_destroy (This->map);
    }
  This->map = map;
  return;
}


/****f* lib5250/tn5250_display_erase_region
 * NAME
 *    tn5250_display_erase_region
 * SYNOPSIS
 *    tn5250_display_erase_region (This, startrow, startcol, endrow, endcol,
 *                                 leftedge, rightedge);
 * INPUTS
 *    Tn5250Display *      This       - 
 *    unsigned int         startrow   - 
 *    unsigned int         startcol   - 
 *    unsigned int         endrow     - 
 *    unsigned int         endcol     - 
 *    unsigned int         leftedge   - 
 *    unsigned int         rightedge  - 
 * DESCRIPTION
 *    Erase a region of the buffer
 *****/
void
tn5250_display_erase_region (Tn5250Display * This, unsigned int startrow,
			     unsigned int startcol, unsigned int endrow,
			     unsigned int endcol, unsigned int leftedge,
			     unsigned int rightedge)
{
  int i, j;

  if (startrow == endrow)
    {
      for (j = startcol - 1; j < endcol; j++)
	{
	  This->display_buffers->
	    data[((startrow - 1) * This->display_buffers->w) + j] =
	    tn5250_char_map_to_remote (This->map, ' ');
	}
    }
  else
    {
      for (i = startrow - 1; i < endrow; i++)
	{
	  if (i == (startrow - 1))
	    {
	      for (j = startcol - 1; j < rightedge; j++)
		{
		  This->display_buffers->data[(i * This->display_buffers->w) +
					      j] =
		    tn5250_char_map_to_remote (This->map, ' ');
		}
	    }
	  else if (i == (endrow - 1))
	    {
	      for (j = leftedge - 1; j < endcol; j++)
		{
		  This->display_buffers->data[(i * This->display_buffers->w) +
					      j] =
		    tn5250_char_map_to_remote (This->map, ' ');
		}
	    }
	  else
	    {
	      for (j = leftedge - 1; j < rightedge; j++)
		{
		  This->display_buffers->data[(i * This->display_buffers->w) +
					      j] =
		    tn5250_char_map_to_remote (This->map, ' ');
		}
	    }
	}
    }
  return;
}


/****f* lib5250/tn5250_display_wordwrap_delete
 * NAME
 *    tn5250_display_wordwrap_delete
 * SYNOPSIS
 *    tn5250_display_wordwrap_delete (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Delete key function.
 *****/
void
tn5250_display_wordwrap_delete (Tn5250Display * This)
{
  Tn5250Field *field = tn5250_display_current_field (This);
  Tn5250Field *iter;
  int buflen;
  unsigned char *text, *ptr;
  unsigned char *data;
  unsigned char espace = TN5250_DISPLAY_WORD_WRAP_SPACE;


  /* Use code from x5250 (with permission).  The basic idea here is to
   * copy all the fields starting with the field we are currently in
   * until the last field in this wordwrap group (the last field isn't
   * marked continuous - but will always be the next field, or so say
   * the docs).  Delete the character at the current cursor location for
   * only the current field.  Then copy the result and concatenate the
   * remaining fields into a single buffer.  Once that is done, word wrap
   * the result and copy it back into the field buffers.
   */

  tn5250_dbuffer_del_this_field_only (This->display_buffers,
				      tn5250_field_count_right (field,
								tn5250_display_cursor_y
								(This),
								tn5250_display_cursor_x
								(This)));

  /* First allocate enough space to do the copying.  This will be sum of
   * the lengths of the word wrap fields in this group starting from the
   * current field.
   */

  /* Find out how much to allocate (do this separately so we can do the
   * actual allocation in one step).
   */
  buflen = 0;
  for (iter = field; tn5250_field_is_wordwrap (iter); iter = iter->next)
    {
      buflen = buflen + tn5250_field_length (iter) + 1;
    }
  buflen = buflen + tn5250_field_length (iter);

  /* Now allocate */
  text = (unsigned char *) malloc (buflen * sizeof (unsigned char));
  ptr = text;

  /* Now repeat the loop above and this time copy the data over.  Insert
   * a space after the end of each field since spaces are implied at the
   * ends of word wrap fields.
   */
  for (iter = field; tn5250_field_is_wordwrap (iter); iter = iter->next)
    {
      data = tn5250_display_field_data (This, iter);
      memcpy (ptr, data, tn5250_field_length (iter) * sizeof (unsigned char));
      ptr = ptr + (tn5250_field_length (iter) * sizeof (unsigned char));
      memcpy (ptr, &espace, sizeof (unsigned char));
      ptr = ptr + sizeof (unsigned char);
    }
  data = tn5250_display_field_data (This, iter);
  memcpy (ptr, data, tn5250_field_length (iter) * sizeof (unsigned char));
  tn5250_display_wordwrap (This, text, buflen, tn5250_field_length (field),
			   field);

  free (text);
  return;
}


/****f* lib5250/tn5250_display_wordwrap_insert
 * NAME
 *    tn5250_display_wordwrap_insert
 * SYNOPSIS
 *    tn5250_display_wordwrap_insert (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Insert key function.
 *****/
void
tn5250_display_wordwrap_insert (Tn5250Display * This, unsigned char c,
				int shiftcount)
{
  Tn5250Field *field = tn5250_display_current_field (This);
  Tn5250Field *iter;
  int x, y, i;
  unsigned char c2;
  int buflen;
  unsigned char *text, *ptr;
  unsigned char *data;
  unsigned char espace = TN5250_DISPLAY_WORD_WRAP_SPACE;

  /* First allocate enough space to do the copying.  This will be sum of
   * the lengths of the word wrap fields in this group starting from the
   * current field.
   */

  /* Find out how much to allocate (do this separately so we can do the
   * actual allocation in one step).
   */
  buflen = 0;

  /* If we just inserted a space near the beginning of a word wrap field
   * that is not the first of a group, we may need to word wrap the prior
   * field as well since we may have just made a word short enough that it
   * should be on the previous line.  All word wrap fields are continuous
   * fields so a check to see if the previous field is a middle field
   * should do the trick.
   */
  if (!tn5250_field_is_continued_first (field))
    {
      iter = field->prev;
    }
  else
    {
      iter = field;
    }
  for (; tn5250_field_is_wordwrap (iter); iter = iter->next)
    {
      buflen = buflen + tn5250_field_length (iter) + 1;
    }
  buflen = buflen + tn5250_field_length (iter);

  /* Now allocate */
  text = (unsigned char *) malloc (buflen * sizeof (unsigned char));
  ptr = text;

  if (!tn5250_field_is_continued_first (field))
    {
      iter = field->prev;
      data = tn5250_display_field_data (This, iter);
      memcpy (ptr, data, tn5250_field_length (iter) * sizeof (unsigned char));
      ptr = ptr + (tn5250_field_length (iter) * sizeof (unsigned char));
      memcpy (ptr, &espace, sizeof (unsigned char));
      ptr = ptr + sizeof (unsigned char);
    }

  /* Use our own version of tn5250_dbuffer_ins().  We can't use the real
   * version because the insert may cause the current field data to be
   * wider than the field.  That's ok because we're going to word wrap it
   * and that will fit the data to the field(s).
   *
   * We need to put the entire field into the buffer, so start at the
   * beginning of the field, then copy the inserted data.
   */
  x = tn5250_field_start_col (field);
  y = tn5250_field_start_row (field);

  for (i = 0; i < tn5250_field_length (field) - shiftcount - 1; i++)
    {
      c2 = This->display_buffers->data[y * This->display_buffers->w + x];
      memcpy (ptr, &c2, sizeof (unsigned char));
      ptr = ptr + sizeof (unsigned char);
      if (++x == This->display_buffers->w)
	{
	  x = 0;
	  y++;
	}
    }

  x = This->display_buffers->cx;
  y = This->display_buffers->cy;

  for (; i < tn5250_field_length (field); i++)
    {
      c2 = This->display_buffers->data[y * This->display_buffers->w + x];
      memcpy (ptr, &c, sizeof (unsigned char));
      ptr = ptr + sizeof (unsigned char);
      c = c2;
      if (++x == This->display_buffers->w)
	{
	  x = 0;
	  y++;
	}
    }
  memcpy (ptr, &c, sizeof (unsigned char));
  ptr = ptr + sizeof (unsigned char);
  memcpy (ptr, &espace, sizeof (unsigned char));
  ptr = ptr + sizeof (unsigned char);

  /* Now repeat the loop above starting with the next field and this time
   * copy the data over.  Insert a space after the end of each field since
   * spaces are implied at the ends of word wrap fields.
   */
  for (iter = field->next; tn5250_field_is_wordwrap (iter); iter = iter->next)
    {
      data = tn5250_display_field_data (This, iter);
      memcpy (ptr, data, tn5250_field_length (iter) * sizeof (unsigned char));
      ptr = ptr + (tn5250_field_length (iter) * sizeof (unsigned char));
      memcpy (ptr, &espace, sizeof (unsigned char));
      ptr = ptr + sizeof (unsigned char);
    }
  data = tn5250_display_field_data (This, iter);
  memcpy (ptr, data, tn5250_field_length (iter) * sizeof (unsigned char));

  if (!tn5250_field_is_continued_first (field))
    {
      tn5250_display_wordwrap (This, text, buflen,
			       tn5250_field_length (field), field->prev);
    }
  else
    {
      tn5250_display_wordwrap (This, text, buflen,
			       tn5250_field_length (field), field);
    }

  tn5250_dbuffer_right (This->display_buffers, 1);

  /* We have to adjust the cursor position ourselves in the case of the
   * cursor being outside the end of the field.
   *
   * Note that we *have* to handle all cursor placement ourselves because
   * tn5250_display_wordwrap() can possibly put the cursor anywhere
   * (don't worry, it is supposed to).  But our caller doesn't have enough
   * info to intelligently place the cursor.
   */
  if (tn5250_display_cursor_x (This) > tn5250_field_end_col (field))
    {
      tn5250_dbuffer_left (This->display_buffers);
      tn5250_display_set_cursor_next_field (This);
    }

  free (text);
  return;
}


/****f* lib5250/tn5250_display_wordwrap_addch
 * NAME
 *    tn5250_display_wordwrap_addch
 * SYNOPSIS
 *    tn5250_display_wordwrap_addch (This);
 * INPUTS
 *    Tn5250Display *      This       - 
 * DESCRIPTION
 *    Addch key function.
 *****/
void
tn5250_display_wordwrap_addch (Tn5250Display * This, unsigned char c)
{
  Tn5250Field *field = tn5250_display_current_field (This);
  Tn5250Field *iter;
  int buflen;
  unsigned char *text, *ptr;
  unsigned char *data;
  unsigned char espace = TN5250_DISPLAY_WORD_WRAP_SPACE;


  /* Use our own version of tn5250_dbuffer_addch().  We can't use the real
   * version because we don't want to advance the cursor position.
   */
  This->display_buffers->
    data[(This->display_buffers->cy * This->display_buffers->w) +
	 This->display_buffers->cx] = c;

  /* First allocate enough space to do the copying.  This will be sum of
   * the lengths of the word wrap fields in this group starting from the
   * current field.
   */

  /* Find out how much to allocate (do this separately so we can do the
   * actual allocation in one step).
   */
  buflen = 0;

  /* If we just added a space near the beginning of a word wrap field
   * that is not the first of a group, we may need to word wrap the prior
   * field as well since we may have just made a word short enough that it
   * should be on the previous line.  All word wrap fields are continuous
   * fields so a check to see if the previous field is a middle field
   * should do the trick.
   */
  if (!tn5250_field_is_continued_first (field))
    {
      iter = field->prev;
    }
  else
    {
      iter = field;
    }
  for (; tn5250_field_is_wordwrap (iter); iter = iter->next)
    {
      buflen = buflen + tn5250_field_length (iter) + 1;
    }
  buflen = buflen + tn5250_field_length (iter);

  /* Now allocate */
  text = (unsigned char *) malloc (buflen * sizeof (unsigned char));
  ptr = text;

  if (!tn5250_field_is_continued_first (field))
    {
      data = tn5250_display_field_data (This, field->prev);
      memcpy (ptr, data,
	      tn5250_field_length (field->prev) * sizeof (unsigned char));
      ptr =
	ptr + (tn5250_field_length (field->prev) * sizeof (unsigned char));
      memcpy (ptr, &espace, sizeof (unsigned char));
      ptr = ptr + sizeof (unsigned char);
    }

  /* Now repeat the loop above starting with the next field and this time
   * copy the data over.  Insert a space after the end of each field since
   * spaces are implied at the ends of word wrap fields.
   */
  for (iter = field; tn5250_field_is_wordwrap (iter); iter = iter->next)
    {
      data = tn5250_display_field_data (This, iter);
      memcpy (ptr, data, tn5250_field_length (iter) * sizeof (unsigned char));
      ptr = ptr + (tn5250_field_length (iter) * sizeof (unsigned char));
      memcpy (ptr, &espace, sizeof (unsigned char));
      ptr = ptr + sizeof (unsigned char);
    }
  data = tn5250_display_field_data (This, iter);
  memcpy (ptr, data, tn5250_field_length (iter) * sizeof (unsigned char));

  if (!tn5250_field_is_continued_first (field))
    {
      tn5250_display_wordwrap (This, text, buflen,
			       tn5250_field_length (field), field->prev);
    }
  else
    {
      tn5250_display_wordwrap (This, text, buflen,
			       tn5250_field_length (field), field);
    }

  tn5250_dbuffer_right (This->display_buffers, 1);

  /* We have to adjust the cursor position ourselves in the case of the
   * cursor being outside the end of the field.
   *
   * Note that we *have* to handle all cursor placement ourselves because
   * tn5250_display_wordwrap() can possibly put the cursor anywhere
   * (don't worry, it is supposed to).  But our caller doesn't have enough
   * info to intelligently place the cursor.
   */
  if (tn5250_display_cursor_x (This) > tn5250_field_end_col (field))
    {
      tn5250_dbuffer_left (This->display_buffers);
      tn5250_display_set_cursor_next_field (This);
    }

  free (text);
  return;
}


void
tn5250_display_wordwrap (Tn5250Display * This, unsigned char *text,
			 int totallen, int fieldlen, Tn5250Field * field)
{
  Tn5250Field *iter;
  int origcursorcol = tn5250_dbuffer_cursor_x (This->display_buffers);
  int nonnullcheck, nonnullcount = 0;
  int cursorset;
  int origentryid = field->entry_id;
  int i, j;
  unsigned char c, c2;
  int curlinelength = 0;
  int wordlength = 0;
  char word[3564 + 1] = { '\0' };
  char line[3564 + 1] = { '\0' };

  /* First find out how many non-null character are between the start
   * of the field and the current cursor position.  This will be used
   * later to set the cursor to the right place after wrapping.
   *
   * Note that the 'i < fieldlen + 1' in the for loop below is because
   * we add an extra character to each field before calling this function.
   */
  j = 0;
  for (iter = field; iter != tn5250_display_current_field (This);
       iter = iter->next)
    {
      for (i = 0; i < fieldlen + 1; i++)
	{
	  if (text[i + j] != TN5250_DISPLAY_WORD_WRAP_SPACE)
	    {
	      nonnullcount++;
	    }
	}
      j = i;
    }

  for (i = 0; i < origcursorcol - tn5250_field_start_col (iter); i++)
    {
      if (text[i + j] != TN5250_DISPLAY_WORD_WRAP_SPACE)
	{
	  nonnullcount++;
	}
    }

  /* After playing a bit with a real IBM terminal I discovered that it
   * treats every space as a word.  Thus everytime we encounter a space
   * complete the current word and add it and a space to the buffer.
   */

  /* We need to be able to distinguish between spaces the user typed in
   * and spaces we add here to pad the end of lines.  Spaces the user
   * typed in should always remain and are treated as one word per space.
   * Spaces we add here for padding should be adjustable.
   *
   * Right now the best way I can think of to do this is to use a special
   * character for spaces we pad with.  Since TN5250_DISPLAY_WORD_WRAP_SPACE
   * is not a valid character in EBCDIC or ASCII I'll use that here.
   */
  iter = field;
  for (i = 0; i < totallen; i++)
    {
      c = text[i];

      if (c != TN5250_DISPLAY_WORD_WRAP_SPACE)
	{
	  c2 = tn5250_char_map_to_local (This->map, c);
	}

      if ((c2 != ' ') && (c != TN5250_DISPLAY_WORD_WRAP_SPACE))
	{
	  word[wordlength] = c2;
	  wordlength++;
	  word[wordlength] = '\0';
	  curlinelength++;
	}
      else
	{
	  if (strlen (line) == 0)
	    {
	      if (c == TN5250_DISPLAY_WORD_WRAP_SPACE)
		{
		  sprintf (line, "%s", word);
		}
	      else
		{
		  sprintf (line, "%s ", word);
		}
	    }
	  else
	    {
	      if ((curlinelength + 1) > fieldlen)
		{
		  tn5250_dbuffer_cursor_set (This->display_buffers,
					     tn5250_field_start_row (iter),
					     tn5250_field_start_col (iter));
		  for (j = 0; j < strlen (line); j++)
		    {
		      tn5250_dbuffer_addch (This->display_buffers,
					    tn5250_char_map_to_remote (This->
								       map,
								       line
								       [j]));
		    }
		  for (; j < tn5250_field_length (iter); j++)
		    {
		      tn5250_dbuffer_addch (This->display_buffers,
					    TN5250_DISPLAY_WORD_WRAP_SPACE);
		    }
		  if (tn5250_field_is_wordwrap (iter))
		    {
		      iter = iter->next;
		    }
		  memset (line, '\0', 133);
		  if (c == TN5250_DISPLAY_WORD_WRAP_SPACE)
		    {
		      sprintf (line, "%s", word);
		    }
		  else
		    {
		      sprintf (line, "%s ", word);
		    }
		  curlinelength = strlen (line);
		}
	      else
		{
		  if (c == TN5250_DISPLAY_WORD_WRAP_SPACE)
		    {
		      sprintf (line, "%s%s", line, word);
		    }
		  else
		    {
		      sprintf (line, "%s%s ", line, word);
		    }
		  curlinelength = strlen (line);
		}
	    }
	  memset (word, '\0', 133);
	  wordlength = 0;
	}
    }

  /* Add or clear any trailing text */
  tn5250_dbuffer_cursor_set (This->display_buffers,
			     tn5250_field_start_row (iter),
			     tn5250_field_start_col (iter));
  if (strlen (word) > 0)
    {
      sprintf (line, "%s%s", line, word);
    }
  for (j = 0; j < strlen (line); j++)
    {
      tn5250_dbuffer_addch (This->display_buffers,
			    tn5250_char_map_to_remote (This->map, line[j]));
    }
  for (; j < tn5250_field_length (iter); j++)
    {
      tn5250_dbuffer_addch (This->display_buffers,
			    TN5250_DISPLAY_WORD_WRAP_SPACE);
    }

  /* And finally clear any old text that might be in fields after the last
   * field we just wrote to in this same word wrap group.
   *
   * It would probably be easier to use the field entry ID here instead of
   * checking to see if the field is continuous and not outside the current
   * word wrap group.
   */
  if (tn5250_field_is_continued_last (iter->next)
      || (tn5250_field_is_wordwrap (iter->next)
	  && !tn5250_field_is_continued_first (iter->next)))
    {
      for (iter = iter->next; tn5250_field_is_wordwrap (iter);
	   iter = iter->next)
	{
	  tn5250_dbuffer_cursor_set (This->display_buffers,
				     tn5250_field_start_row (iter),
				     tn5250_field_start_col (iter));
	  for (j = 0; j < tn5250_field_length (iter); j++)
	    {
	      tn5250_dbuffer_addch (This->display_buffers,
				    TN5250_DISPLAY_WORD_WRAP_SPACE);
	    }
	}
      if (tn5250_field_is_continued_last (iter))
	{
	  tn5250_dbuffer_cursor_set (This->display_buffers,
				     tn5250_field_start_row (iter),
				     tn5250_field_start_col (iter));
	  for (j = 0; j < tn5250_field_length (iter); j++)
	    {
	      tn5250_dbuffer_addch (This->display_buffers,
				    TN5250_DISPLAY_WORD_WRAP_SPACE);
	    }
	}
    }

  /* And finally put the cursor where it should be. */
  nonnullcheck = 0;
  cursorset = 0;
  for (iter = field; iter->entry_id == origentryid; iter = iter->next)
    {
      i = tn5250_field_start_row (iter);

      for (j = tn5250_field_start_col (iter);
	   j <= tn5250_field_end_col (iter); j++)
	{
	  if (j == This->display_buffers->w)
	    {
	      j = 0;
	      i++;
	    }
	  if (tn5250_display_char_at (This, i, j) !=
	      TN5250_DISPLAY_WORD_WRAP_SPACE)
	    {
	      if (nonnullcheck >= nonnullcount)
		{
		  tn5250_dbuffer_cursor_set (This->display_buffers, i, j);
		  cursorset = 1;
		  break;
		}
	      nonnullcheck++;
	    }
	}
      if (cursorset)
	{
	  break;
	}
    }

  return;
}


syntax highlighted by Code2HTML, v. 0.9.1