Base version of ncurses-5.7 library
diff --git a/form/frm_driver.c b/form/frm_driver.c
new file mode 100644
index 0000000..94323ed
--- /dev/null
+++ b/form/frm_driver.c
@@ -0,0 +1,4576 @@
+/****************************************************************************
+ * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc.              *
+ *                                                                          *
+ * Permission is hereby granted, free of charge, to any person obtaining a  *
+ * copy of this software and associated documentation files (the            *
+ * "Software"), to deal in the Software without restriction, including      *
+ * without limitation the rights to use, copy, modify, merge, publish,      *
+ * distribute, distribute with modifications, sublicense, and/or sell       *
+ * copies of the Software, and to permit persons to whom the Software is    *
+ * furnished to do so, subject to the following conditions:                 *
+ *                                                                          *
+ * The above copyright notice and this permission notice shall be included  *
+ * in all copies or substantial portions of the Software.                   *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
+ * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
+ *                                                                          *
+ * Except as contained in this notice, the name(s) of the above copyright   *
+ * holders shall not be used in advertising or otherwise to promote the     *
+ * sale, use or other dealings in this Software without prior written       *
+ * authorization.                                                           *
+ ****************************************************************************/
+
+/****************************************************************************
+ *   Author:  Juergen Pfeifer, 1995,1997                                    *
+ ****************************************************************************/
+
+#include "form.priv.h"
+
+MODULE_ID("$Id: frm_driver.c,v 1.88 2008/10/18 16:25:00 tom Exp $")
+
+/*----------------------------------------------------------------------------
+  This is the core module of the form library. It contains the majority
+  of the driver routines as well as the form_driver function.
+
+  Essentially this module is nearly the whole library. This is because
+  all the functions in this module depends on some others in the module,
+  so it makes no sense to split them into separate files because they
+  will always be linked together. The only acceptable concern is turnaround
+  time for this module, but now we have all Pentiums or RISCs, so what!
+
+  The driver routines are grouped into nine generic categories:
+
+   a)   Page Navigation            ( all functions prefixed by PN_ )
+        The current page of the form is left and some new page is
+        entered.
+   b)   Inter-Field Navigation     ( all functions prefixed by FN_ )
+        The current field of the form is left and some new field is
+        entered.
+   c)   Intra-Field Navigation     ( all functions prefixed by IFN_ )
+        The current position in the current field is changed.
+   d)   Vertical Scrolling         ( all functions prefixed by VSC_ )
+        Essentially this is a specialization of Intra-Field navigation.
+        It has to check for a multi-line field.
+   e)   Horizontal Scrolling       ( all functions prefixed by HSC_ )
+        Essentially this is a specialization of Intra-Field navigation.
+        It has to check for a single-line field.
+   f)   Field Editing              ( all functions prefixed by FE_ )
+        The content of the current field is changed
+   g)   Edit Mode requests         ( all functions prefixed by EM_ )
+        Switching between insert and overlay mode
+   h)   Field-Validation requests  ( all functions prefixed by FV_ )
+        Perform verifications of the field.
+   i)   Choice requests            ( all functions prefixed by CR_ )
+        Requests to enumerate possible field values
+  --------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------
+  Some remarks on the placements of assert() macros :
+  I use them only on "strategic" places, i.e. top level entries where
+  I want to make sure that things are set correctly. Throughout subordinate
+  routines I omit them mostly.
+  --------------------------------------------------------------------------*/
+
+/*
+Some options that may effect compatibility in behavior to SVr4 forms,
+but they are here to allow a more intuitive and user friendly behavior of
+our form implementation. This doesn't affect the API, so we feel it is
+uncritical.
+
+The initial implementation tries to stay very close with the behavior
+of the original SVr4 implementation, although in some areas it is quite
+clear that this isn't the most appropriate way. As far as possible this
+sources will allow you to build a forms lib that behaves quite similar
+to SVr4, but now and in the future we will give you better options.
+Perhaps at some time we will make this configurable at runtime.
+*/
+
+/* Implement a more user-friendly previous/next word behavior */
+#define FRIENDLY_PREV_NEXT_WORD (1)
+/* Fix the wrong behavior for forms with all fields inactive */
+#define FIX_FORM_INACTIVE_BUG (1)
+/* Allow dynamic field growth also when navigating past the end */
+#define GROW_IF_NAVIGATE (1)
+
+#if USE_WIDEC_SUPPORT
+#define myADDNSTR(w, s, n) wadd_wchnstr(w, s, n)
+#define myINSNSTR(w, s, n) wins_wchnstr(w, s, n)
+#define myINNSTR(w, s, n)  fix_wchnstr(w, s, n)
+#define myWCWIDTH(w, y, x) cell_width(w, y, x)
+#else
+#define myADDNSTR(w, s, n) waddnstr(w, s, n)
+#define myINSNSTR(w, s, n) winsnstr(w, s, n)
+#define myINNSTR(w, s, n)  winnstr(w, s, n)
+#define myWCWIDTH(w, y, x) 1
+#endif
+
+/*----------------------------------------------------------------------------
+  Forward references to some internally used static functions
+  --------------------------------------------------------------------------*/
+static int Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form);
+static int FN_Next_Field(FORM *form);
+static int FN_Previous_Field(FORM *form);
+static int FE_New_Line(FORM *);
+static int FE_Delete_Previous(FORM *);
+
+/*----------------------------------------------------------------------------
+  Macro Definitions.
+
+  Some Remarks on that: I use the convention to use UPPERCASE for constants
+  defined by Macros. If I provide a macro as a kind of inline routine to
+  provide some logic, I use my Upper_Lower case style.
+  --------------------------------------------------------------------------*/
+
+/* Calculate the position of a single row in a field buffer */
+#define Position_Of_Row_In_Buffer(field,row) ((row)*(field)->dcols)
+
+/* Calculate start address for the fields buffer# N */
+#define Address_Of_Nth_Buffer(field,N) \
+  ((field)->buf + (N)*(1+Buffer_Length(field)))
+
+/* Calculate the start address of the row in the fields specified buffer# N */
+#define Address_Of_Row_In_Nth_Buffer(field,N,row) \
+  (Address_Of_Nth_Buffer(field,N) + Position_Of_Row_In_Buffer(field,row))
+
+/* Calculate the start address of the row in the fields primary buffer */
+#define Address_Of_Row_In_Buffer(field,row) \
+  Address_Of_Row_In_Nth_Buffer(field,0,row)
+
+/* Calculate the start address of the row in the forms current field
+   buffer# N */
+#define Address_Of_Current_Row_In_Nth_Buffer(form,N) \
+   Address_Of_Row_In_Nth_Buffer((form)->current,N,(form)->currow)
+
+/* Calculate the start address of the row in the forms current field
+   primary buffer */
+#define Address_Of_Current_Row_In_Buffer(form) \
+   Address_Of_Current_Row_In_Nth_Buffer(form,0)
+
+/* Calculate the address of the cursor in the forms current field
+   primary buffer */
+#define Address_Of_Current_Position_In_Nth_Buffer(form,N) \
+   (Address_Of_Current_Row_In_Nth_Buffer(form,N) + (form)->curcol)
+
+/* Calculate the address of the cursor in the forms current field
+   buffer# N */
+#define Address_Of_Current_Position_In_Buffer(form) \
+  Address_Of_Current_Position_In_Nth_Buffer(form,0)
+
+/* Logic to decide whether or not a field is actually a field with
+   vertical or horizontal scrolling */
+#define Is_Scroll_Field(field)          \
+   (((field)->drows > (field)->rows) || \
+    ((field)->dcols > (field)->cols))
+
+/* Logic to decide whether or not a field needs to have an individual window
+   instead of a derived window because it contains invisible parts.
+   This is true for non-public fields and for scrollable fields. */
+#define Has_Invisible_Parts(field)     \
+  (!((field)->opts & O_PUBLIC)      || \
+   Is_Scroll_Field(field))
+
+/* Logic to decide whether or not a field needs justification */
+#define Justification_Allowed(field)        \
+   (((field)->just != NO_JUSTIFICATION)  && \
+    (Single_Line_Field(field))           && \
+    (((field)->dcols == (field)->cols)   && \
+    ((field)->opts & O_STATIC))             )
+
+/* Logic to determine whether or not a dynamic field may still grow */
+#define Growable(field) ((field)->status & _MAY_GROW)
+
+/* Macro to set the attributes for a fields window */
+#define Set_Field_Window_Attributes(field,win) \
+(  wbkgdset((win),(chtype)((field)->pad | (field)->back)), \
+   wattrset((win),(field)->fore) )
+
+/* Logic to decide whether or not a field really appears on the form */
+#define Field_Really_Appears(field)         \
+  ((field->form)                          &&\
+   (field->form->status & _POSTED)        &&\
+   (field->opts & O_VISIBLE)              &&\
+   (field->page == field->form->curpage))
+
+/* Logic to determine whether or not we are on the first position in the
+   current field */
+#define First_Position_In_Current_Field(form) \
+  (((form)->currow==0) && ((form)->curcol==0))
+
+#define Minimum(a,b) (((a)<=(b)) ? (a) : (b))
+#define Maximum(a,b) (((a)>=(b)) ? (a) : (b))
+
+/*----------------------------------------------------------------------------
+  Useful constants
+  --------------------------------------------------------------------------*/
+static FIELD_CELL myBLANK = BLANK;
+static FIELD_CELL myZEROS;
+
+#ifdef TRACE
+static void
+check_pos(FORM *form, int lineno)
+{
+  int y, x;
+
+  if (form && form->w)
+    {
+      getyx(form->w, y, x);
+      if (y != form->currow || x != form->curcol)
+	{
+	  T(("CHECKPOS %s@%d have position %d,%d vs want %d,%d",
+	     __FILE__, lineno,
+	     y, x,
+	     form->currow, form->curcol));
+	}
+    }
+}
+#define CHECKPOS(form) check_pos(form, __LINE__)
+#else
+#define CHECKPOS(form)		/* nothing */
+#endif
+
+/*----------------------------------------------------------------------------
+  Wide-character special functions
+  --------------------------------------------------------------------------*/
+#if USE_WIDEC_SUPPORT
+/* like winsnstr */
+static int
+wins_wchnstr(WINDOW *w, cchar_t *s, int n)
+{
+  int code = ERR;
+  int y, x;
+
+  while (n-- > 0)
+    {
+      getyx(w, y, x);
+      if ((code = wins_wch(w, s++)) != OK)
+	break;
+      if ((code = wmove(w, y, x + 1)) != OK)
+	break;
+    }
+  return code;
+}
+
+/* win_wchnstr is inconsistent with winnstr, since it returns OK rather than
+ * the number of items transferred.
+ */
+static int
+fix_wchnstr(WINDOW *w, cchar_t *s, int n)
+{
+  int x;
+
+  win_wchnstr(w, s, n);
+  /*
+   * This function is used to extract the text only from the window.
+   * Strip attributes and color from the string so they will not be added
+   * back when copying the string to the window.
+   */
+  for (x = 0; x < n; ++x)
+    {
+      RemAttr(s[x], A_ATTRIBUTES);
+      SetPair(s[x], 0);
+    }
+  return n;
+}
+
+/*
+ * Returns the column of the base of the given cell.
+ */
+static int
+cell_base(WINDOW *win, int y, int x)
+{
+  int result = x;
+
+  while (LEGALYX(win, y, x))
+    {
+      cchar_t *data = &(win->_line[y].text[x]);
+
+      if (isWidecBase(CHDEREF(data)) || !isWidecExt(CHDEREF(data)))
+	{
+	  result = x;
+	  break;
+	}
+      --x;
+    }
+  return result;
+}
+
+/*
+ * Returns the number of columns needed for the given cell in a window.
+ */
+static int
+cell_width(WINDOW *win, int y, int x)
+{
+  int result = 1;
+
+  if (LEGALYX(win, y, x))
+    {
+      cchar_t *data = &(win->_line[y].text[x]);
+
+      if (isWidecExt(CHDEREF(data)))
+	{
+	  /* recur, providing the number of columns to the next character */
+	  result = cell_width(win, y, x - 1);
+	}
+      else
+	{
+	  result = wcwidth(CharOf(CHDEREF(data)));
+	}
+    }
+  return result;
+}
+
+/*
+ * There is no wide-character function such as wdel_wch(), so we must find
+ * all of the cells that comprise a multi-column character and delete them
+ * one-by-one.
+ */
+static void
+delete_char(FORM *form)
+{
+  int cells = cell_width(form->w, form->currow, form->curcol);
+
+  form->curcol = cell_base(form->w, form->currow, form->curcol);
+  wmove(form->w, form->currow, form->curcol);
+  while (cells-- > 0)
+    {
+      wdelch(form->w);
+    }
+}
+#define DeleteChar(form) delete_char(form)
+#else
+#define DeleteChar(form) \
+	  wmove((form)->w, (form)->currow, (form)->curcol), \
+	  wdelch((form)->w)
+#endif
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static char *Get_Start_Of_Data(char * buf, int blen)
+|
+|   Description   :  Return pointer to first non-blank position in buffer.
+|                    If buffer is empty return pointer to buffer itself.
+|
+|   Return Values :  Pointer to first non-blank position in buffer
++--------------------------------------------------------------------------*/
+NCURSES_INLINE static FIELD_CELL *
+Get_Start_Of_Data(FIELD_CELL *buf, int blen)
+{
+  FIELD_CELL *p = buf;
+  FIELD_CELL *end = &buf[blen];
+
+  assert(buf && blen >= 0);
+  while ((p < end) && ISBLANK(*p))
+    p++;
+  return ((p == end) ? buf : p);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static char *After_End_Of_Data(char * buf, int blen)
+|
+|   Description   :  Return pointer after last non-blank position in buffer.
+|                    If buffer is empty, return pointer to buffer itself.
+|
+|   Return Values :  Pointer to position after last non-blank position in
+|                    buffer.
++--------------------------------------------------------------------------*/
+NCURSES_INLINE static FIELD_CELL *
+After_End_Of_Data(FIELD_CELL *buf, int blen)
+{
+  FIELD_CELL *p = &buf[blen];
+
+  assert(buf && blen >= 0);
+  while ((p > buf) && ISBLANK(p[-1]))
+    p--;
+  return (p);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static char *Get_First_Whitespace_Character(
+|                                     char * buf, int   blen)
+|
+|   Description   :  Position to the first whitespace character.
+|
+|   Return Values :  Pointer to first whitespace character in buffer.
++--------------------------------------------------------------------------*/
+NCURSES_INLINE static FIELD_CELL *
+Get_First_Whitespace_Character(FIELD_CELL *buf, int blen)
+{
+  FIELD_CELL *p = buf;
+  FIELD_CELL *end = &p[blen];
+
+  assert(buf && blen >= 0);
+  while ((p < end) && !ISBLANK(*p))
+    p++;
+  return ((p == end) ? buf : p);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static char *After_Last_Whitespace_Character(
+|                                     char * buf, int blen)
+|
+|   Description   :  Get the position after the last whitespace character.
+|
+|   Return Values :  Pointer to position after last whitespace character in
+|                    buffer.
++--------------------------------------------------------------------------*/
+NCURSES_INLINE static FIELD_CELL *
+After_Last_Whitespace_Character(FIELD_CELL *buf, int blen)
+{
+  FIELD_CELL *p = &buf[blen];
+
+  assert(buf && blen >= 0);
+  while ((p > buf) && !ISBLANK(p[-1]))
+    p--;
+  return (p);
+}
+
+/* Set this to 1 to use the div_t version. This is a good idea if your
+   compiler has an intrinsic div() support. Unfortunately GNU-C has it
+   not yet.
+   N.B.: This only works if form->curcol follows immediately form->currow
+         and both are of type int.
+*/
+#define USE_DIV_T (0)
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static void Adjust_Cursor_Position(
+|                                       FORM * form, const char * pos)
+|
+|   Description   :  Set current row and column of the form to values
+|                    corresponding to the buffer position.
+|
+|   Return Values :  -
++--------------------------------------------------------------------------*/
+NCURSES_INLINE static void
+Adjust_Cursor_Position(FORM *form, const FIELD_CELL *pos)
+{
+  FIELD *field;
+  int idx;
+
+  field = form->current;
+  assert(pos >= field->buf && field->dcols > 0);
+  idx = (int)(pos - field->buf);
+#if USE_DIV_T
+  *((div_t *) & (form->currow)) = div(idx, field->dcols);
+#else
+  form->currow = idx / field->dcols;
+  form->curcol = idx - field->cols * form->currow;
+#endif
+  if (field->drows < form->currow)
+    form->currow = 0;
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static void Buffer_To_Window(
+|                                      const FIELD  * field,
+|                                      WINDOW * win)
+|
+|   Description   :  Copy the buffer to the window. If it is a multi-line
+|                    field, the buffer is split to the lines of the
+|                    window without any editing.
+|
+|   Return Values :  -
++--------------------------------------------------------------------------*/
+static void
+Buffer_To_Window(const FIELD *field, WINDOW *win)
+{
+  int width, height;
+  int y, x;
+  int len;
+  int row;
+  FIELD_CELL *pBuffer;
+
+  assert(win && field);
+
+  getyx(win, y, x);
+  width = getmaxx(win);
+  height = getmaxy(win);
+
+  for (row = 0, pBuffer = field->buf;
+       row < height;
+       row++, pBuffer += width)
+    {
+      if ((len = (int)(After_End_Of_Data(pBuffer, width) - pBuffer)) > 0)
+	{
+	  wmove(win, row, 0);
+	  myADDNSTR(win, pBuffer, len);
+	}
+    }
+  wmove(win, y, x);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static void Window_To_Buffer(
+|                                          WINDOW * win,
+|                                          FIELD  * field)
+|
+|   Description   :  Copy the content of the window into the buffer.
+|                    The multiple lines of a window are simply
+|                    concatenated into the buffer. Pad characters in
+|                    the window will be replaced by blanks in the buffer.
+|
+|   Return Values :  -
++--------------------------------------------------------------------------*/
+static void
+Window_To_Buffer(WINDOW *win, FIELD *field)
+{
+  int pad;
+  int len = 0;
+  FIELD_CELL *p;
+  int row, height;
+
+  assert(win && field && field->buf);
+
+  pad = field->pad;
+  p = field->buf;
+  height = getmaxy(win);
+
+  for (row = 0; (row < height) && (row < field->drows); row++)
+    {
+      wmove(win, row, 0);
+      len += myINNSTR(win, p + len, field->dcols);
+    }
+  p[len] = myZEROS;
+
+  /* replace visual padding character by blanks in buffer */
+  if (pad != C_BLANK)
+    {
+      int i;
+
+      for (i = 0; i < len; i++, p++)
+	{
+	  if ((unsigned long)CharOf(*p) == ChCharOf(pad)
+#if USE_WIDEC_SUPPORT
+	      && p->chars[1] == 0
+#endif
+	    )
+	    *p = myBLANK;
+	}
+    }
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static void Synchronize_Buffer(FORM * form)
+|
+|   Description   :  If there was a change, copy the content of the
+|                    window into the buffer, so the buffer is synchronized
+|                    with the windows content. We have to indicate that the
+|                    buffer needs validation due to the change.
+|
+|   Return Values :  -
++--------------------------------------------------------------------------*/
+NCURSES_INLINE static void
+Synchronize_Buffer(FORM *form)
+{
+  if (form->status & _WINDOW_MODIFIED)
+    {
+      form->status &= ~_WINDOW_MODIFIED;
+      form->status |= _FCHECK_REQUIRED;
+      Window_To_Buffer(form->w, form->current);
+      wmove(form->w, form->currow, form->curcol);
+    }
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static bool Field_Grown( FIELD *field, int amount)
+|
+|   Description   :  This function is called for growable dynamic fields
+|                    only. It has to increase the buffers and to allocate
+|                    a new window for this field.
+|                    This function has the side effect to set a new
+|                    field-buffer pointer, the dcols and drows values
+|                    as well as a new current Window for the field.
+|
+|   Return Values :  TRUE     - field successfully increased
+|                    FALSE    - there was some error
++--------------------------------------------------------------------------*/
+static bool
+Field_Grown(FIELD *field, int amount)
+{
+  bool result = FALSE;
+
+  if (field && Growable(field))
+    {
+      bool single_line_field = Single_Line_Field(field);
+      int old_buflen = Buffer_Length(field);
+      int new_buflen;
+      int old_dcols = field->dcols;
+      int old_drows = field->drows;
+      FIELD_CELL *oldbuf = field->buf;
+      FIELD_CELL *newbuf;
+
+      int growth;
+      FORM *form = field->form;
+      bool need_visual_update = ((form != (FORM *)0) &&
+				 (form->status & _POSTED) &&
+				 (form->current == field));
+
+      if (need_visual_update)
+	Synchronize_Buffer(form);
+
+      if (single_line_field)
+	{
+	  growth = field->cols * amount;
+	  if (field->maxgrow)
+	    growth = Minimum(field->maxgrow - field->dcols, growth);
+	  field->dcols += growth;
+	  if (field->dcols == field->maxgrow)
+	    field->status &= ~_MAY_GROW;
+	}
+      else
+	{
+	  growth = (field->rows + field->nrow) * amount;
+	  if (field->maxgrow)
+	    growth = Minimum(field->maxgrow - field->drows, growth);
+	  field->drows += growth;
+	  if (field->drows == field->maxgrow)
+	    field->status &= ~_MAY_GROW;
+	}
+      /* drows, dcols changed, so we get really the new buffer length */
+      new_buflen = Buffer_Length(field);
+      newbuf = (FIELD_CELL *)malloc(Total_Buffer_Size(field));
+      if (!newbuf)
+	{
+	  /* restore to previous state */
+	  field->dcols = old_dcols;
+	  field->drows = old_drows;
+	  if ((single_line_field && (field->dcols != field->maxgrow)) ||
+	      (!single_line_field && (field->drows != field->maxgrow)))
+	    field->status |= _MAY_GROW;
+	}
+      else
+	{
+	  /* Copy all the buffers.  This is the reason why we can't just use
+	   * realloc().
+	   */
+	  int i, j;
+	  FIELD_CELL *old_bp;
+	  FIELD_CELL *new_bp;
+
+	  result = TRUE;	/* allow sharing of recovery on failure */
+
+	  T((T_CREATE("fieldcell %p"), newbuf));
+	  field->buf = newbuf;
+	  for (i = 0; i <= field->nbuf; i++)
+	    {
+	      new_bp = Address_Of_Nth_Buffer(field, i);
+	      old_bp = oldbuf + i * (1 + old_buflen);
+	      for (j = 0; j < old_buflen; ++j)
+		new_bp[j] = old_bp[j];
+	      while (j < new_buflen)
+		new_bp[j++] = myBLANK;
+	      new_bp[new_buflen] = myZEROS;
+	    }
+
+#if USE_WIDEC_SUPPORT && NCURSES_EXT_FUNCS
+	  if (wresize(field->working, 1, Buffer_Length(field) + 1) == ERR)
+	    result = FALSE;
+#endif
+
+	  if (need_visual_update && result)
+	    {
+	      WINDOW *new_window = newpad(field->drows, field->dcols);
+
+	      if (new_window != 0)
+		{
+		  assert(form != (FORM *)0);
+		  if (form->w)
+		    delwin(form->w);
+		  form->w = new_window;
+		  Set_Field_Window_Attributes(field, form->w);
+		  werase(form->w);
+		  Buffer_To_Window(field, form->w);
+		  untouchwin(form->w);
+		  wmove(form->w, form->currow, form->curcol);
+		}
+	      else
+		result = FALSE;
+	    }
+
+	  if (result)
+	    {
+	      free(oldbuf);
+	      /* reflect changes in linked fields */
+	      if (field != field->link)
+		{
+		  FIELD *linked_field;
+
+		  for (linked_field = field->link;
+		       linked_field != field;
+		       linked_field = linked_field->link)
+		    {
+		      linked_field->buf = field->buf;
+		      linked_field->drows = field->drows;
+		      linked_field->dcols = field->dcols;
+		    }
+		}
+	    }
+	  else
+	    {
+	      /* restore old state */
+	      field->dcols = old_dcols;
+	      field->drows = old_drows;
+	      field->buf = oldbuf;
+	      if ((single_line_field &&
+		   (field->dcols != field->maxgrow)) ||
+		  (!single_line_field &&
+		   (field->drows != field->maxgrow)))
+		field->status |= _MAY_GROW;
+	      free(newbuf);
+	    }
+	}
+    }
+  return (result);
+}
+
+#ifdef NCURSES_MOUSE_VERSION
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  int Field_encloses(FIELD *field, int ry, int rx)
+|
+|   Description   :  Check if the given coordinates lie within the given field.
+|
+|   Return Values :  E_OK              - success
+|                    E_BAD_ARGUMENT    - invalid form pointer
+|                    E_SYSTEM_ERROR    - form has no current field or
+|                                        field-window
++--------------------------------------------------------------------------*/
+static int
+Field_encloses(FIELD *field, int ry, int rx)
+{
+  T((T_CALLED("Field_encloses(%p)"), field));
+  if (field != 0
+      && field->frow <= ry
+      && (field->frow + field->rows) > ry
+      && field->fcol <= rx
+      && (field->fcol + field->cols) > rx)
+    {
+      RETURN(E_OK);
+    }
+  RETURN(E_INVALID_FIELD);
+}
+#endif
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  int _nc_Position_Form_Cursor(FORM * form)
+|
+|   Description   :  Position the cursor in the window for the current
+|                    field to be in sync. with the currow and curcol
+|                    values.
+|
+|   Return Values :  E_OK              - success
+|                    E_BAD_ARGUMENT    - invalid form pointer
+|                    E_SYSTEM_ERROR    - form has no current field or
+|                                        field-window
++--------------------------------------------------------------------------*/
+NCURSES_EXPORT(int)
+_nc_Position_Form_Cursor(FORM *form)
+{
+  FIELD *field;
+  WINDOW *formwin;
+
+  if (!form)
+    return (E_BAD_ARGUMENT);
+
+  if (!form->w || !form->current)
+    return (E_SYSTEM_ERROR);
+
+  field = form->current;
+  formwin = Get_Form_Window(form);
+
+  wmove(form->w, form->currow, form->curcol);
+  if (Has_Invisible_Parts(field))
+    {
+      /* in this case fieldwin isn't derived from formwin, so we have
+         to move the cursor in formwin by hand... */
+      wmove(formwin,
+	    field->frow + form->currow - form->toprow,
+	    field->fcol + form->curcol - form->begincol);
+      wcursyncup(formwin);
+    }
+  else
+    wcursyncup(form->w);
+  return (E_OK);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  int _nc_Refresh_Current_Field(FORM * form)
+|
+|   Description   :  Propagate the changes in the fields window to the
+|                    window of the form.
+|
+|   Return Values :  E_OK              - on success
+|                    E_BAD_ARGUMENT    - invalid form pointer
+|                    E_SYSTEM_ERROR    - general error
++--------------------------------------------------------------------------*/
+NCURSES_EXPORT(int)
+_nc_Refresh_Current_Field(FORM *form)
+{
+  WINDOW *formwin;
+  FIELD *field;
+
+  T((T_CALLED("_nc_Refresh_Current_Field(%p)"), form));
+
+  if (!form)
+    RETURN(E_BAD_ARGUMENT);
+
+  if (!form->w || !form->current)
+    RETURN(E_SYSTEM_ERROR);
+
+  field = form->current;
+  formwin = Get_Form_Window(form);
+
+  if (field->opts & O_PUBLIC)
+    {
+      if (Is_Scroll_Field(field))
+	{
+	  /* Again, in this case the fieldwin isn't derived from formwin,
+	     so we have to perform a copy operation. */
+	  if (Single_Line_Field(field))
+	    {
+	      /* horizontal scrolling */
+	      if (form->curcol < form->begincol)
+		form->begincol = form->curcol;
+	      else
+		{
+		  if (form->curcol >= (form->begincol + field->cols))
+		    form->begincol = form->curcol - field->cols + 1;
+		}
+	      copywin(form->w,
+		      formwin,
+		      0,
+		      form->begincol,
+		      field->frow,
+		      field->fcol,
+		      field->frow,
+		      field->cols + field->fcol - 1,
+		      0);
+	    }
+	  else
+	    {
+	      /* A multi-line, i.e. vertical scrolling field */
+	      int row_after_bottom, first_modified_row, first_unmodified_row;
+
+	      if (field->drows > field->rows)
+		{
+		  row_after_bottom = form->toprow + field->rows;
+		  if (form->currow < form->toprow)
+		    {
+		      form->toprow = form->currow;
+		      field->status |= _NEWTOP;
+		    }
+		  if (form->currow >= row_after_bottom)
+		    {
+		      form->toprow = form->currow - field->rows + 1;
+		      field->status |= _NEWTOP;
+		    }
+		  if (field->status & _NEWTOP)
+		    {
+		      /* means we have to copy whole range */
+		      first_modified_row = form->toprow;
+		      first_unmodified_row = first_modified_row + field->rows;
+		      field->status &= ~_NEWTOP;
+		    }
+		  else
+		    {
+		      /* we try to optimize : finding the range of touched
+		         lines */
+		      first_modified_row = form->toprow;
+		      while (first_modified_row < row_after_bottom)
+			{
+			  if (is_linetouched(form->w, first_modified_row))
+			    break;
+			  first_modified_row++;
+			}
+		      first_unmodified_row = first_modified_row;
+		      while (first_unmodified_row < row_after_bottom)
+			{
+			  if (!is_linetouched(form->w, first_unmodified_row))
+			    break;
+			  first_unmodified_row++;
+			}
+		    }
+		}
+	      else
+		{
+		  first_modified_row = form->toprow;
+		  first_unmodified_row = first_modified_row + field->rows;
+		}
+	      if (first_unmodified_row != first_modified_row)
+		copywin(form->w,
+			formwin,
+			first_modified_row,
+			0,
+			field->frow + first_modified_row - form->toprow,
+			field->fcol,
+			field->frow + first_unmodified_row - form->toprow - 1,
+			field->cols + field->fcol - 1,
+			0);
+	    }
+	  wsyncup(formwin);
+	}
+      else
+	{
+	  /* if the field-window is simply a derived window, i.e. contains no
+	   * invisible parts, the whole thing is trivial
+	   */
+	  wsyncup(form->w);
+	}
+    }
+  untouchwin(form->w);
+  returnCode(_nc_Position_Form_Cursor(form));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static void Perform_Justification(
+|                                        FIELD  * field,
+|                                        WINDOW * win)
+|
+|   Description   :  Output field with requested justification
+|
+|   Return Values :  -
++--------------------------------------------------------------------------*/
+static void
+Perform_Justification(FIELD *field, WINDOW *win)
+{
+  FIELD_CELL *bp;
+  int len;
+  int col = 0;
+
+  bp = Get_Start_Of_Data(field->buf, Buffer_Length(field));
+  len = (int)(After_End_Of_Data(field->buf, Buffer_Length(field)) - bp);
+
+  if (len > 0)
+    {
+      assert(win && (field->drows == 1) && (field->dcols == field->cols));
+
+      switch (field->just)
+	{
+	case JUSTIFY_LEFT:
+	  break;
+	case JUSTIFY_CENTER:
+	  col = (field->cols - len) / 2;
+	  break;
+	case JUSTIFY_RIGHT:
+	  col = field->cols - len;
+	  break;
+	default:
+	  break;
+	}
+
+      wmove(win, 0, col);
+      myADDNSTR(win, bp, len);
+    }
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static void Undo_Justification(
+|                                     FIELD  * field,
+|                                     WINDOW * win)
+|
+|   Description   :  Display field without any justification, i.e.
+|                    left justified
+|
+|   Return Values :  -
++--------------------------------------------------------------------------*/
+static void
+Undo_Justification(FIELD *field, WINDOW *win)
+{
+  FIELD_CELL *bp;
+  int len;
+
+  bp = Get_Start_Of_Data(field->buf, Buffer_Length(field));
+  len = (int)(After_End_Of_Data(field->buf, Buffer_Length(field)) - bp);
+
+  if (len > 0)
+    {
+      assert(win);
+      wmove(win, 0, 0);
+      myADDNSTR(win, bp, len);
+    }
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static bool Check_Char(
+|                                           FIELDTYPE * typ,
+|                                           int ch,
+|                                           TypeArgument *argp)
+|
+|   Description   :  Perform a single character check for character ch
+|                    according to the fieldtype instance.
+|
+|   Return Values :  TRUE             - Character is valid
+|                    FALSE            - Character is invalid
++--------------------------------------------------------------------------*/
+static bool
+Check_Char(FIELDTYPE *typ, int ch, TypeArgument *argp)
+{
+  if (typ)
+    {
+      if (typ->status & _LINKED_TYPE)
+	{
+	  assert(argp);
+	  return (
+		   Check_Char(typ->left, ch, argp->left) ||
+		   Check_Char(typ->right, ch, argp->right));
+	}
+      else
+	{
+	  if (typ->ccheck)
+	    return typ->ccheck(ch, (void *)argp);
+	}
+    }
+  return (!iscntrl(UChar(ch)) ? TRUE : FALSE);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int Display_Or_Erase_Field(
+|                                           FIELD * field,
+|                                           bool bEraseFlag)
+|
+|   Description   :  Create a subwindow for the field and display the
+|                    buffer contents (apply justification if required)
+|                    or simply erase the field.
+|
+|   Return Values :  E_OK           - on success
+|                    E_SYSTEM_ERROR - some error (typical no memory)
++--------------------------------------------------------------------------*/
+static int
+Display_Or_Erase_Field(FIELD *field, bool bEraseFlag)
+{
+  WINDOW *win;
+  WINDOW *fwin;
+
+  if (!field)
+    return E_SYSTEM_ERROR;
+
+  fwin = Get_Form_Window(field->form);
+  win = derwin(fwin,
+	       field->rows, field->cols, field->frow, field->fcol);
+
+  if (!win)
+    return E_SYSTEM_ERROR;
+  else
+    {
+      if (field->opts & O_VISIBLE)
+	Set_Field_Window_Attributes(field, win);
+      else
+	wattrset(win, WINDOW_ATTRS(fwin));
+      werase(win);
+    }
+
+  if (!bEraseFlag)
+    {
+      if (field->opts & O_PUBLIC)
+	{
+	  if (Justification_Allowed(field))
+	    Perform_Justification(field, win);
+	  else
+	    Buffer_To_Window(field, win);
+	}
+      field->status &= ~_NEWTOP;
+    }
+  wsyncup(win);
+  delwin(win);
+  return E_OK;
+}
+
+/* Macros to preset the bEraseFlag */
+#define Display_Field(field) Display_Or_Erase_Field(field,FALSE)
+#define Erase_Field(field)   Display_Or_Erase_Field(field,TRUE)
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int Synchronize_Field(FIELD * field)
+|
+|   Description   :  Synchronize the windows content with the value in
+|                    the buffer.
+|
+|   Return Values :  E_OK                - success
+|                    E_BAD_ARGUMENT      - invalid field pointer
+|                    E_SYSTEM_ERROR      - some severe basic error
++--------------------------------------------------------------------------*/
+static int
+Synchronize_Field(FIELD *field)
+{
+  FORM *form;
+  int res = E_OK;
+
+  if (!field)
+    return (E_BAD_ARGUMENT);
+
+  if (((form = field->form) != (FORM *)0)
+      && Field_Really_Appears(field))
+    {
+      if (field == form->current)
+	{
+	  form->currow = form->curcol = form->toprow = form->begincol = 0;
+	  werase(form->w);
+
+	  if ((field->opts & O_PUBLIC) && Justification_Allowed(field))
+	    Undo_Justification(field, form->w);
+	  else
+	    Buffer_To_Window(field, form->w);
+
+	  field->status |= _NEWTOP;
+	  res = _nc_Refresh_Current_Field(form);
+	}
+      else
+	res = Display_Field(field);
+    }
+  field->status |= _CHANGED;
+  return (res);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int Synchronize_Linked_Fields(FIELD * field)
+|
+|   Description   :  Propagate the Synchronize_Field function to all linked
+|                    fields. The first error that occurs in the sequence
+|                    of updates is the return value.
+|
+|   Return Values :  E_OK                - success
+|                    E_BAD_ARGUMENT      - invalid field pointer
+|                    E_SYSTEM_ERROR      - some severe basic error
++--------------------------------------------------------------------------*/
+static int
+Synchronize_Linked_Fields(FIELD *field)
+{
+  FIELD *linked_field;
+  int res = E_OK;
+  int syncres;
+
+  if (!field)
+    return (E_BAD_ARGUMENT);
+
+  if (!field->link)
+    return (E_SYSTEM_ERROR);
+
+  for (linked_field = field->link;
+       linked_field != field;
+       linked_field = linked_field->link)
+    {
+      if (((syncres = Synchronize_Field(linked_field)) != E_OK) &&
+	  (res == E_OK))
+	res = syncres;
+    }
+  return (res);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  int _nc_Synchronize_Attributes(FIELD * field)
+|
+|   Description   :  If a fields visual attributes have changed, this
+|                    routine is called to propagate those changes to the
+|                    screen.
+|
+|   Return Values :  E_OK             - success
+|                    E_BAD_ARGUMENT   - invalid field pointer
+|                    E_SYSTEM_ERROR   - some severe basic error
++--------------------------------------------------------------------------*/
+NCURSES_EXPORT(int)
+_nc_Synchronize_Attributes(FIELD *field)
+{
+  FORM *form;
+  int res = E_OK;
+  WINDOW *formwin;
+
+  T((T_CALLED("_nc_Synchronize_Attributes(%p)"), field));
+
+  if (!field)
+    returnCode(E_BAD_ARGUMENT);
+
+  CHECKPOS(field->form);
+  if (((form = field->form) != (FORM *)0)
+      && Field_Really_Appears(field))
+    {
+      if (form->current == field)
+	{
+	  Synchronize_Buffer(form);
+	  Set_Field_Window_Attributes(field, form->w);
+	  werase(form->w);
+	  wmove(form->w, form->currow, form->curcol);
+
+	  if (field->opts & O_PUBLIC)
+	    {
+	      if (Justification_Allowed(field))
+		Undo_Justification(field, form->w);
+	      else
+		Buffer_To_Window(field, form->w);
+	    }
+	  else
+	    {
+	      formwin = Get_Form_Window(form);
+	      copywin(form->w, formwin,
+		      0, 0,
+		      field->frow, field->fcol,
+		      field->rows - 1, field->cols - 1, 0);
+	      wsyncup(formwin);
+	      Buffer_To_Window(field, form->w);
+	      field->status |= _NEWTOP;		/* fake refresh to paint all */
+	      _nc_Refresh_Current_Field(form);
+	    }
+	}
+      else
+	{
+	  res = Display_Field(field);
+	}
+    }
+  CHECKPOS(form);
+  returnCode(res);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  int _nc_Synchronize_Options(FIELD * field,
+|                                                Field_Options newopts)
+|
+|   Description   :  If a fields options have changed, this routine is
+|                    called to propagate these changes to the screen and
+|                    to really change the behavior of the field.
+|
+|   Return Values :  E_OK                - success
+|                    E_BAD_ARGUMENT      - invalid field pointer
+|                    E_CURRENT           - field is the current one
+|                    E_SYSTEM_ERROR      - some severe basic error
++--------------------------------------------------------------------------*/
+NCURSES_EXPORT(int)
+_nc_Synchronize_Options(FIELD *field, Field_Options newopts)
+{
+  Field_Options oldopts;
+  Field_Options changed_opts;
+  FORM *form;
+  int res = E_OK;
+
+  T((T_CALLED("_nc_Synchronize_Options(%p,%#x)"), field, newopts));
+
+  if (!field)
+    returnCode(E_BAD_ARGUMENT);
+
+  oldopts = field->opts;
+  changed_opts = oldopts ^ newopts;
+  field->opts = newopts;
+  form = field->form;
+
+  if (form)
+    {
+      if (form->current == field)
+	{
+	  field->opts = oldopts;
+	  returnCode(E_CURRENT);
+	}
+
+      if (form->status & _POSTED)
+	{
+	  if ((form->curpage == field->page))
+	    {
+	      if (changed_opts & O_VISIBLE)
+		{
+		  if (newopts & O_VISIBLE)
+		    res = Display_Field(field);
+		  else
+		    res = Erase_Field(field);
+		}
+	      else
+		{
+		  if ((changed_opts & O_PUBLIC) &&
+		      (newopts & O_VISIBLE))
+		    res = Display_Field(field);
+		}
+	    }
+	}
+    }
+
+  if (changed_opts & O_STATIC)
+    {
+      bool single_line_field = Single_Line_Field(field);
+      int res2 = E_OK;
+
+      if (newopts & O_STATIC)
+	{
+	  /* the field becomes now static */
+	  field->status &= ~_MAY_GROW;
+	  /* if actually we have no hidden columns, justification may
+	     occur again */
+	  if (single_line_field &&
+	      (field->cols == field->dcols) &&
+	      (field->just != NO_JUSTIFICATION) &&
+	      Field_Really_Appears(field))
+	    {
+	      res2 = Display_Field(field);
+	    }
+	}
+      else
+	{
+	  /* field is no longer static */
+	  if ((field->maxgrow == 0) ||
+	      (single_line_field && (field->dcols < field->maxgrow)) ||
+	      (!single_line_field && (field->drows < field->maxgrow)))
+	    {
+	      field->status |= _MAY_GROW;
+	      /* a field with justification now changes its behavior,
+	         so we must redisplay it */
+	      if (single_line_field &&
+		  (field->just != NO_JUSTIFICATION) &&
+		  Field_Really_Appears(field))
+		{
+		  res2 = Display_Field(field);
+		}
+	    }
+	}
+      if (res2 != E_OK)
+	res = res2;
+    }
+
+  returnCode(res);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  int _nc_Set_Current_Field(FORM  * form,
+|                                              FIELD * newfield)
+|
+|   Description   :  Make the newfield the new current field.
+|
+|   Return Values :  E_OK              - success
+|                    E_BAD_ARGUMENT    - invalid form or field pointer
+|                    E_SYSTEM_ERROR    - some severe basic error
+|                    E_NOT_CONNECTED   - no fields are connected to the form
++--------------------------------------------------------------------------*/
+NCURSES_EXPORT(int)
+_nc_Set_Current_Field(FORM *form, FIELD *newfield)
+{
+  FIELD *field;
+  WINDOW *new_window;
+
+  T((T_CALLED("_nc_Set_Current_Field(%p,%p)"), form, newfield));
+
+  if (!form || !newfield || !form->current || (newfield->form != form))
+    returnCode(E_BAD_ARGUMENT);
+
+  if ((form->status & _IN_DRIVER))
+    returnCode(E_BAD_STATE);
+
+  if (!(form->field))
+    returnCode(E_NOT_CONNECTED);
+
+  field = form->current;
+
+  if ((field != newfield) ||
+      !(form->status & _POSTED))
+    {
+      if ((form->w) &&
+	  (field->opts & O_VISIBLE) &&
+	  (field->form->curpage == field->page))
+	{
+	  _nc_Refresh_Current_Field(form);
+	  if (field->opts & O_PUBLIC)
+	    {
+	      if (field->drows > field->rows)
+		{
+		  if (form->toprow == 0)
+		    field->status &= ~_NEWTOP;
+		  else
+		    field->status |= _NEWTOP;
+		}
+	      else
+		{
+		  if (Justification_Allowed(field))
+		    {
+		      Window_To_Buffer(form->w, field);
+		      werase(form->w);
+		      Perform_Justification(field, form->w);
+		      wsyncup(form->w);
+		    }
+		}
+	    }
+	  delwin(form->w);
+	  form->w = (WINDOW *)0;
+	}
+
+      field = newfield;
+
+      if (Has_Invisible_Parts(field))
+	new_window = newpad(field->drows, field->dcols);
+      else
+	new_window = derwin(Get_Form_Window(form),
+			    field->rows, field->cols, field->frow, field->fcol);
+
+      if (!new_window)
+	returnCode(E_SYSTEM_ERROR);
+
+      form->current = field;
+
+      if (form->w)
+	delwin(form->w);
+      form->w = new_window;
+
+      form->status &= ~_WINDOW_MODIFIED;
+      Set_Field_Window_Attributes(field, form->w);
+
+      if (Has_Invisible_Parts(field))
+	{
+	  werase(form->w);
+	  Buffer_To_Window(field, form->w);
+	}
+      else
+	{
+	  if (Justification_Allowed(field))
+	    {
+	      werase(form->w);
+	      Undo_Justification(field, form->w);
+	      wsyncup(form->w);
+	    }
+	}
+
+      untouchwin(form->w);
+    }
+
+  form->currow = form->curcol = form->toprow = form->begincol = 0;
+  returnCode(E_OK);
+}
+
+/*----------------------------------------------------------------------------
+  Intra-Field Navigation routines
+  --------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int IFN_Next_Character(FORM * form)
+|
+|   Description   :  Move to the next character in the field. In a multi-line
+|                    field this wraps at the end of the line.
+|
+|   Return Values :  E_OK                - success
+|                    E_REQUEST_DENIED    - at the rightmost position
++--------------------------------------------------------------------------*/
+static int
+IFN_Next_Character(FORM *form)
+{
+  FIELD *field = form->current;
+  int step = myWCWIDTH(form->w, form->currow, form->curcol);
+
+  T((T_CALLED("IFN_Next_Character(%p)"), form));
+  if ((form->curcol += step) == field->dcols)
+    {
+      if ((++(form->currow)) == field->drows)
+	{
+#if GROW_IF_NAVIGATE
+	  if (!Single_Line_Field(field) && Field_Grown(field, 1))
+	    {
+	      form->curcol = 0;
+	      returnCode(E_OK);
+	    }
+#endif
+	  form->currow--;
+#if GROW_IF_NAVIGATE
+	  if (Single_Line_Field(field) && Field_Grown(field, 1))
+	    returnCode(E_OK);
+#endif
+	  form->curcol -= step;
+	  returnCode(E_REQUEST_DENIED);
+	}
+      form->curcol = 0;
+    }
+  returnCode(E_OK);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int IFN_Previous_Character(FORM * form)
+|
+|   Description   :  Move to the previous character in the field. In a
+|                    multi-line field this wraps and the beginning of the
+|                    line.
+|
+|   Return Values :  E_OK                - success
+|                    E_REQUEST_DENIED    - at the leftmost position
++--------------------------------------------------------------------------*/
+static int
+IFN_Previous_Character(FORM *form)
+{
+  int amount = myWCWIDTH(form->w, form->currow, form->curcol - 1);
+  int oldcol = form->curcol;
+
+  T((T_CALLED("IFN_Previous_Character(%p)"), form));
+  if ((form->curcol -= amount) < 0)
+    {
+      if ((--(form->currow)) < 0)
+	{
+	  form->currow++;
+	  form->curcol = oldcol;
+	  returnCode(E_REQUEST_DENIED);
+	}
+      form->curcol = form->current->dcols - 1;
+    }
+  returnCode(E_OK);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int IFN_Next_Line(FORM * form)
+|
+|   Description   :  Move to the beginning of the next line in the field
+|
+|   Return Values :  E_OK                - success
+|                    E_REQUEST_DENIED    - at the last line
++--------------------------------------------------------------------------*/
+static int
+IFN_Next_Line(FORM *form)
+{
+  FIELD *field = form->current;
+
+  T((T_CALLED("IFN_Next_Line(%p)"), form));
+  if ((++(form->currow)) == field->drows)
+    {
+#if GROW_IF_NAVIGATE
+      if (!Single_Line_Field(field) && Field_Grown(field, 1))
+	returnCode(E_OK);
+#endif
+      form->currow--;
+      returnCode(E_REQUEST_DENIED);
+    }
+  form->curcol = 0;
+  returnCode(E_OK);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int IFN_Previous_Line(FORM * form)
+|
+|   Description   :  Move to the beginning of the previous line in the field
+|
+|   Return Values :  E_OK                - success
+|                    E_REQUEST_DENIED    - at the first line
++--------------------------------------------------------------------------*/
+static int
+IFN_Previous_Line(FORM *form)
+{
+  T((T_CALLED("IFN_Previous_Line(%p)"), form));
+  if ((--(form->currow)) < 0)
+    {
+      form->currow++;
+      returnCode(E_REQUEST_DENIED);
+    }
+  form->curcol = 0;
+  returnCode(E_OK);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int IFN_Next_Word(FORM * form)
+|
+|   Description   :  Move to the beginning of the next word in the field.
+|
+|   Return Values :  E_OK             - success
+|                    E_REQUEST_DENIED - there is no next word
++--------------------------------------------------------------------------*/
+static int
+IFN_Next_Word(FORM *form)
+{
+  FIELD *field = form->current;
+  FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
+  FIELD_CELL *s;
+  FIELD_CELL *t;
+
+  T((T_CALLED("IFN_Next_Word(%p)"), form));
+
+  /* We really need access to the data, so we have to synchronize */
+  Synchronize_Buffer(form);
+
+  /* Go to the first whitespace after the current position (including
+     current position). This is then the starting point to look for the
+     next non-blank data */
+  s = Get_First_Whitespace_Character(bp, Buffer_Length(field) -
+				     (int)(bp - field->buf));
+
+  /* Find the start of the next word */
+  t = Get_Start_Of_Data(s, Buffer_Length(field) -
+			(int)(s - field->buf));
+#if !FRIENDLY_PREV_NEXT_WORD
+  if (s == t)
+    returnCode(E_REQUEST_DENIED);
+  else
+#endif
+    {
+      Adjust_Cursor_Position(form, t);
+      returnCode(E_OK);
+    }
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int IFN_Previous_Word(FORM * form)
+|
+|   Description   :  Move to the beginning of the previous word in the field.
+|
+|   Return Values :  E_OK             - success
+|                    E_REQUEST_DENIED - there is no previous word
++--------------------------------------------------------------------------*/
+static int
+IFN_Previous_Word(FORM *form)
+{
+  FIELD *field = form->current;
+  FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
+  FIELD_CELL *s;
+  FIELD_CELL *t;
+  bool again = FALSE;
+
+  T((T_CALLED("IFN_Previous_Word(%p)"), form));
+
+  /* We really need access to the data, so we have to synchronize */
+  Synchronize_Buffer(form);
+
+  s = After_End_Of_Data(field->buf, (int)(bp - field->buf));
+  /* s points now right after the last non-blank in the buffer before bp.
+     If bp was in a word, s equals bp. In this case we must find the last
+     whitespace in the buffer before bp and repeat the game to really find
+     the previous word! */
+  if (s == bp)
+    again = TRUE;
+
+  /* And next call now goes backward to look for the last whitespace
+     before that, pointing right after this, so it points to the begin
+     of the previous word.
+   */
+  t = After_Last_Whitespace_Character(field->buf, (int)(s - field->buf));
+#if !FRIENDLY_PREV_NEXT_WORD
+  if (s == t)
+    returnCode(E_REQUEST_DENIED);
+#endif
+  if (again)
+    {
+      /* and do it again, replacing bp by t */
+      s = After_End_Of_Data(field->buf, (int)(t - field->buf));
+      t = After_Last_Whitespace_Character(field->buf, (int)(s - field->buf));
+#if !FRIENDLY_PREV_NEXT_WORD
+      if (s == t)
+	returnCode(E_REQUEST_DENIED);
+#endif
+    }
+  Adjust_Cursor_Position(form, t);
+  returnCode(E_OK);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int IFN_Beginning_Of_Field(FORM * form)
+|
+|   Description   :  Place the cursor at the first non-pad character in
+|                    the field.
+|
+|   Return Values :  E_OK             - success
++--------------------------------------------------------------------------*/
+static int
+IFN_Beginning_Of_Field(FORM *form)
+{
+  FIELD *field = form->current;
+
+  T((T_CALLED("IFN_Beginning_Of_Field(%p)"), form));
+  Synchronize_Buffer(form);
+  Adjust_Cursor_Position(form,
+			 Get_Start_Of_Data(field->buf, Buffer_Length(field)));
+  returnCode(E_OK);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int IFN_End_Of_Field(FORM * form)
+|
+|   Description   :  Place the cursor after the last non-pad character in
+|                    the field. If the field occupies the last position in
+|                    the buffer, the cursor is positioned on the last
+|                    character.
+|
+|   Return Values :  E_OK              - success
++--------------------------------------------------------------------------*/
+static int
+IFN_End_Of_Field(FORM *form)
+{
+  FIELD *field = form->current;
+  FIELD_CELL *pos;
+
+  T((T_CALLED("IFN_End_Of_Field(%p)"), form));
+  Synchronize_Buffer(form);
+  pos = After_End_Of_Data(field->buf, Buffer_Length(field));
+  if (pos == (field->buf + Buffer_Length(field)))
+    pos--;
+  Adjust_Cursor_Position(form, pos);
+  returnCode(E_OK);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int IFN_Beginning_Of_Line(FORM * form)
+|
+|   Description   :  Place the cursor on the first non-pad character in
+|                    the current line of the field.
+|
+|   Return Values :  E_OK         - success
++--------------------------------------------------------------------------*/
+static int
+IFN_Beginning_Of_Line(FORM *form)
+{
+  FIELD *field = form->current;
+
+  T((T_CALLED("IFN_Beginning_Of_Line(%p)"), form));
+  Synchronize_Buffer(form);
+  Adjust_Cursor_Position(form,
+			 Get_Start_Of_Data(Address_Of_Current_Row_In_Buffer(form),
+					   field->dcols));
+  returnCode(E_OK);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int IFN_End_Of_Line(FORM * form)
+|
+|   Description   :  Place the cursor after the last non-pad character in the
+|                    current line of the field. If the field occupies the
+|                    last column in the line, the cursor is positioned on the
+|                    last character of the line.
+|
+|   Return Values :  E_OK        - success
++--------------------------------------------------------------------------*/
+static int
+IFN_End_Of_Line(FORM *form)
+{
+  FIELD *field = form->current;
+  FIELD_CELL *pos;
+  FIELD_CELL *bp;
+
+  T((T_CALLED("IFN_End_Of_Line(%p)"), form));
+  Synchronize_Buffer(form);
+  bp = Address_Of_Current_Row_In_Buffer(form);
+  pos = After_End_Of_Data(bp, field->dcols);
+  if (pos == (bp + field->dcols))
+    pos--;
+  Adjust_Cursor_Position(form, pos);
+  returnCode(E_OK);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int IFN_Left_Character(FORM * form)
+|
+|   Description   :  Move one character to the left in the current line.
+|                    This doesn't cycle.
+|
+|   Return Values :  E_OK             - success
+|                    E_REQUEST_DENIED - already in first column
++--------------------------------------------------------------------------*/
+static int
+IFN_Left_Character(FORM *form)
+{
+  int amount = myWCWIDTH(form->w, form->currow, form->curcol - 1);
+  int oldcol = form->curcol;
+
+  T((T_CALLED("IFN_Left_Character(%p)"), form));
+  if ((form->curcol -= amount) < 0)
+    {
+      form->curcol = oldcol;
+      returnCode(E_REQUEST_DENIED);
+    }
+  returnCode(E_OK);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int IFN_Right_Character(FORM * form)
+|
+|   Description   :  Move one character to the right in the current line.
+|                    This doesn't cycle.
+|
+|   Return Values :  E_OK              - success
+|                    E_REQUEST_DENIED  - already in last column
++--------------------------------------------------------------------------*/
+static int
+IFN_Right_Character(FORM *form)
+{
+  int amount = myWCWIDTH(form->w, form->currow, form->curcol);
+  int oldcol = form->curcol;
+
+  T((T_CALLED("IFN_Right_Character(%p)"), form));
+  if ((form->curcol += amount) >= form->current->dcols)
+    {
+#if GROW_IF_NAVIGATE
+      FIELD *field = form->current;
+
+      if (Single_Line_Field(field) && Field_Grown(field, 1))
+	returnCode(E_OK);
+#endif
+      form->curcol = oldcol;
+      returnCode(E_REQUEST_DENIED);
+    }
+  returnCode(E_OK);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int IFN_Up_Character(FORM * form)
+|
+|   Description   :  Move one line up. This doesn't cycle through the lines
+|                    of the field.
+|
+|   Return Values :  E_OK              - success
+|                    E_REQUEST_DENIED  - already in last column
++--------------------------------------------------------------------------*/
+static int
+IFN_Up_Character(FORM *form)
+{
+  T((T_CALLED("IFN_Up_Character(%p)"), form));
+  if ((--(form->currow)) < 0)
+    {
+      form->currow++;
+      returnCode(E_REQUEST_DENIED);
+    }
+  returnCode(E_OK);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int IFN_Down_Character(FORM * form)
+|
+|   Description   :  Move one line down. This doesn't cycle through the
+|                    lines of the field.
+|
+|   Return Values :  E_OK              - success
+|                    E_REQUEST_DENIED  - already in last column
++--------------------------------------------------------------------------*/
+static int
+IFN_Down_Character(FORM *form)
+{
+  FIELD *field = form->current;
+
+  T((T_CALLED("IFN_Down_Character(%p)"), form));
+  if ((++(form->currow)) == field->drows)
+    {
+#if GROW_IF_NAVIGATE
+      if (!Single_Line_Field(field) && Field_Grown(field, 1))
+	returnCode(E_OK);
+#endif
+      --(form->currow);
+      returnCode(E_REQUEST_DENIED);
+    }
+  returnCode(E_OK);
+}
+/*----------------------------------------------------------------------------
+  END of Intra-Field Navigation routines
+  --------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------
+  Vertical scrolling helper routines
+  --------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int VSC_Generic(FORM *form, int nlines)
+|
+|   Description   :  Scroll multi-line field forward (nlines>0) or
+|                    backward (nlines<0) this many lines.
+|
+|   Return Values :  E_OK              - success
+|                    E_REQUEST_DENIED  - can't scroll
++--------------------------------------------------------------------------*/
+static int
+VSC_Generic(FORM *form, int nlines)
+{
+  FIELD *field = form->current;
+  int res = E_REQUEST_DENIED;
+  int rows_to_go = (nlines > 0 ? nlines : -nlines);
+
+  if (nlines > 0)
+    {
+      if ((rows_to_go + form->toprow) > (field->drows - field->rows))
+	rows_to_go = (field->drows - field->rows - form->toprow);
+
+      if (rows_to_go > 0)
+	{
+	  form->currow += rows_to_go;
+	  form->toprow += rows_to_go;
+	  res = E_OK;
+	}
+    }
+  else
+    {
+      if (rows_to_go > form->toprow)
+	rows_to_go = form->toprow;
+
+      if (rows_to_go > 0)
+	{
+	  form->currow -= rows_to_go;
+	  form->toprow -= rows_to_go;
+	  res = E_OK;
+	}
+    }
+  return (res);
+}
+/*----------------------------------------------------------------------------
+  End of Vertical scrolling helper routines
+  --------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------
+  Vertical scrolling routines
+  --------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int Vertical_Scrolling(
+|                                           int (* const fct) (FORM *),
+|                                           FORM * form)
+|
+|   Description   :  Performs the generic vertical scrolling routines.
+|                    This has to check for a multi-line field and to set
+|                    the _NEWTOP flag if scrolling really occurred.
+|
+|   Return Values :  Propagated error code from low-level driver calls
++--------------------------------------------------------------------------*/
+static int
+Vertical_Scrolling(int (*const fct) (FORM *), FORM *form)
+{
+  int res = E_REQUEST_DENIED;
+
+  if (!Single_Line_Field(form->current))
+    {
+      res = fct(form);
+      if (res == E_OK)
+	form->current->status |= _NEWTOP;
+    }
+  return (res);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int VSC_Scroll_Line_Forward(FORM * form)
+|
+|   Description   :  Scroll multi-line field forward a line
+|
+|   Return Values :  E_OK                - success
+|                    E_REQUEST_DENIED    - no data ahead
++--------------------------------------------------------------------------*/
+static int
+VSC_Scroll_Line_Forward(FORM *form)
+{
+  T((T_CALLED("VSC_Scroll_Line_Forward(%p)"), form));
+  returnCode(VSC_Generic(form, 1));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int VSC_Scroll_Line_Backward(FORM * form)
+|
+|   Description   :  Scroll multi-line field backward a line
+|
+|   Return Values :  E_OK                - success
+|                    E_REQUEST_DENIED    - no data behind
++--------------------------------------------------------------------------*/
+static int
+VSC_Scroll_Line_Backward(FORM *form)
+{
+  T((T_CALLED("VSC_Scroll_Line_Backward(%p)"), form));
+  returnCode(VSC_Generic(form, -1));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int VSC_Scroll_Page_Forward(FORM * form)
+|
+|   Description   :  Scroll a multi-line field forward a page
+|
+|   Return Values :  E_OK              - success
+|                    E_REQUEST_DENIED  - no data ahead
++--------------------------------------------------------------------------*/
+static int
+VSC_Scroll_Page_Forward(FORM *form)
+{
+  T((T_CALLED("VSC_Scroll_Page_Forward(%p)"), form));
+  returnCode(VSC_Generic(form, form->current->rows));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int VSC_Scroll_Half_Page_Forward(FORM * form)
+|
+|   Description   :  Scroll a multi-line field forward half a page
+|
+|   Return Values :  E_OK              - success
+|                    E_REQUEST_DENIED  - no data ahead
++--------------------------------------------------------------------------*/
+static int
+VSC_Scroll_Half_Page_Forward(FORM *form)
+{
+  T((T_CALLED("VSC_Scroll_Half_Page_Forward(%p)"), form));
+  returnCode(VSC_Generic(form, (form->current->rows + 1) / 2));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int VSC_Scroll_Page_Backward(FORM * form)
+|
+|   Description   :  Scroll a multi-line field backward a page
+|
+|   Return Values :  E_OK              - success
+|                    E_REQUEST_DENIED  - no data behind
++--------------------------------------------------------------------------*/
+static int
+VSC_Scroll_Page_Backward(FORM *form)
+{
+  T((T_CALLED("VSC_Scroll_Page_Backward(%p)"), form));
+  returnCode(VSC_Generic(form, -(form->current->rows)));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int VSC_Scroll_Half_Page_Backward(FORM * form)
+|
+|   Description   :  Scroll a multi-line field backward half a page
+|
+|   Return Values :  E_OK              - success
+|                    E_REQUEST_DENIED  - no data behind
++--------------------------------------------------------------------------*/
+static int
+VSC_Scroll_Half_Page_Backward(FORM *form)
+{
+  T((T_CALLED("VSC_Scroll_Half_Page_Backward(%p)"), form));
+  returnCode(VSC_Generic(form, -((form->current->rows + 1) / 2)));
+}
+/*----------------------------------------------------------------------------
+  End of Vertical scrolling routines
+  --------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------
+  Horizontal scrolling helper routines
+  --------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int HSC_Generic(FORM *form, int ncolumns)
+|
+|   Description   :  Scroll single-line field forward (ncolumns>0) or
+|                    backward (ncolumns<0) this many columns.
+|
+|   Return Values :  E_OK              - success
+|                    E_REQUEST_DENIED  - can't scroll
++--------------------------------------------------------------------------*/
+static int
+HSC_Generic(FORM *form, int ncolumns)
+{
+  FIELD *field = form->current;
+  int res = E_REQUEST_DENIED;
+  int cols_to_go = (ncolumns > 0 ? ncolumns : -ncolumns);
+
+  if (ncolumns > 0)
+    {
+      if ((cols_to_go + form->begincol) > (field->dcols - field->cols))
+	cols_to_go = field->dcols - field->cols - form->begincol;
+
+      if (cols_to_go > 0)
+	{
+	  form->curcol += cols_to_go;
+	  form->begincol += cols_to_go;
+	  res = E_OK;
+	}
+    }
+  else
+    {
+      if (cols_to_go > form->begincol)
+	cols_to_go = form->begincol;
+
+      if (cols_to_go > 0)
+	{
+	  form->curcol -= cols_to_go;
+	  form->begincol -= cols_to_go;
+	  res = E_OK;
+	}
+    }
+  return (res);
+}
+/*----------------------------------------------------------------------------
+  End of Horizontal scrolling helper routines
+  --------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------
+  Horizontal scrolling routines
+  --------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int Horizontal_Scrolling(
+|                                          int (* const fct) (FORM *),
+|                                          FORM * form)
+|
+|   Description   :  Performs the generic horizontal scrolling routines.
+|                    This has to check for a single-line field.
+|
+|   Return Values :  Propagated error code from low-level driver calls
++--------------------------------------------------------------------------*/
+static int
+Horizontal_Scrolling(int (*const fct) (FORM *), FORM *form)
+{
+  if (Single_Line_Field(form->current))
+    return fct(form);
+  else
+    return (E_REQUEST_DENIED);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int HSC_Scroll_Char_Forward(FORM * form)
+|
+|   Description   :  Scroll single-line field forward a character
+|
+|   Return Values :  E_OK                - success
+|                    E_REQUEST_DENIED    - no data ahead
++--------------------------------------------------------------------------*/
+static int
+HSC_Scroll_Char_Forward(FORM *form)
+{
+  T((T_CALLED("HSC_Scroll_Char_Forward(%p)"), form));
+  returnCode(HSC_Generic(form, 1));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int HSC_Scroll_Char_Backward(FORM * form)
+|
+|   Description   :  Scroll single-line field backward a character
+|
+|   Return Values :  E_OK                - success
+|                    E_REQUEST_DENIED    - no data behind
++--------------------------------------------------------------------------*/
+static int
+HSC_Scroll_Char_Backward(FORM *form)
+{
+  T((T_CALLED("HSC_Scroll_Char_Backward(%p)"), form));
+  returnCode(HSC_Generic(form, -1));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int HSC_Horizontal_Line_Forward(FORM* form)
+|
+|   Description   :  Scroll single-line field forward a line
+|
+|   Return Values :  E_OK                - success
+|                    E_REQUEST_DENIED    - no data ahead
++--------------------------------------------------------------------------*/
+static int
+HSC_Horizontal_Line_Forward(FORM *form)
+{
+  T((T_CALLED("HSC_Horizontal_Line_Forward(%p)"), form));
+  returnCode(HSC_Generic(form, form->current->cols));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int HSC_Horizontal_Half_Line_Forward(FORM* form)
+|
+|   Description   :  Scroll single-line field forward half a line
+|
+|   Return Values :  E_OK               - success
+|                    E_REQUEST_DENIED   - no data ahead
++--------------------------------------------------------------------------*/
+static int
+HSC_Horizontal_Half_Line_Forward(FORM *form)
+{
+  T((T_CALLED("HSC_Horizontal_Half_Line_Forward(%p)"), form));
+  returnCode(HSC_Generic(form, (form->current->cols + 1) / 2));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int HSC_Horizontal_Line_Backward(FORM* form)
+|
+|   Description   :  Scroll single-line field backward a line
+|
+|   Return Values :  E_OK                - success
+|                    E_REQUEST_DENIED    - no data behind
++--------------------------------------------------------------------------*/
+static int
+HSC_Horizontal_Line_Backward(FORM *form)
+{
+  T((T_CALLED("HSC_Horizontal_Line_Backward(%p)"), form));
+  returnCode(HSC_Generic(form, -(form->current->cols)));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int HSC_Horizontal_Half_Line_Backward(FORM* form)
+|
+|   Description   :  Scroll single-line field backward half a line
+|
+|   Return Values :  E_OK                - success
+|                    E_REQUEST_DENIED    - no data behind
++--------------------------------------------------------------------------*/
+static int
+HSC_Horizontal_Half_Line_Backward(FORM *form)
+{
+  T((T_CALLED("HSC_Horizontal_Half_Line_Backward(%p)"), form));
+  returnCode(HSC_Generic(form, -((form->current->cols + 1) / 2)));
+}
+
+/*----------------------------------------------------------------------------
+  End of Horizontal scrolling routines
+  --------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------
+  Helper routines for Field Editing
+  --------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static bool Is_There_Room_For_A_Line(FORM * form)
+|
+|   Description   :  Check whether or not there is enough room in the
+|                    buffer to enter a whole line.
+|
+|   Return Values :  TRUE   - there is enough space
+|                    FALSE  - there is not enough space
++--------------------------------------------------------------------------*/
+NCURSES_INLINE static bool
+Is_There_Room_For_A_Line(FORM *form)
+{
+  FIELD *field = form->current;
+  FIELD_CELL *begin_of_last_line, *s;
+
+  Synchronize_Buffer(form);
+  begin_of_last_line = Address_Of_Row_In_Buffer(field, (field->drows - 1));
+  s = After_End_Of_Data(begin_of_last_line, field->dcols);
+  return ((s == begin_of_last_line) ? TRUE : FALSE);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static bool Is_There_Room_For_A_Char_In_Line(FORM * form)
+|
+|   Description   :  Checks whether or not there is room for a new character
+|                    in the current line.
+|
+|   Return Values :  TRUE    - there is room
+|                    FALSE   - there is not enough room (line full)
++--------------------------------------------------------------------------*/
+NCURSES_INLINE static bool
+Is_There_Room_For_A_Char_In_Line(FORM *form)
+{
+  int last_char_in_line;
+
+  wmove(form->w, form->currow, form->current->dcols - 1);
+  last_char_in_line = (int)(winch(form->w) & A_CHARTEXT);
+  wmove(form->w, form->currow, form->curcol);
+  return (((last_char_in_line == form->current->pad) ||
+	   is_blank(last_char_in_line)) ? TRUE : FALSE);
+}
+
+#define There_Is_No_Room_For_A_Char_In_Line(f) \
+  !Is_There_Room_For_A_Char_In_Line(f)
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int Insert_String(
+|                                             FORM * form,
+|                                             int row,
+|                                             char *txt,
+|                                             int  len )
+|
+|   Description   :  Insert the 'len' characters beginning at pointer 'txt'
+|                    into the 'row' of the 'form'. The insertion occurs
+|                    on the beginning of the row, all other characters are
+|                    moved to the right. After the text a pad character will
+|                    be inserted to separate the text from the rest. If
+|                    necessary the insertion moves characters on the next
+|                    line to make place for the requested insertion string.
+|
+|   Return Values :  E_OK              - success
+|                    E_REQUEST_DENIED  -
+|                    E_SYSTEM_ERROR    - system error
++--------------------------------------------------------------------------*/
+static int
+Insert_String(FORM *form, int row, FIELD_CELL *txt, int len)
+{
+  FIELD *field = form->current;
+  FIELD_CELL *bp = Address_Of_Row_In_Buffer(field, row);
+  int datalen = (int)(After_End_Of_Data(bp, field->dcols) - bp);
+  int freelen = field->dcols - datalen;
+  int requiredlen = len + 1;
+  FIELD_CELL *split;
+  int result = E_REQUEST_DENIED;
+
+  if (freelen >= requiredlen)
+    {
+      wmove(form->w, row, 0);
+      myINSNSTR(form->w, txt, len);
+      wmove(form->w, row, len);
+      myINSNSTR(form->w, &myBLANK, 1);
+      return E_OK;
+    }
+  else
+    {
+      /* we have to move characters on the next line. If we are on the
+         last line this may work, if the field is growable */
+      if ((row == (field->drows - 1)) && Growable(field))
+	{
+	  if (!Field_Grown(field, 1))
+	    return (E_SYSTEM_ERROR);
+	  /* !!!Side-Effect : might be changed due to growth!!! */
+	  bp = Address_Of_Row_In_Buffer(field, row);
+	}
+
+      if (row < (field->drows - 1))
+	{
+	  split =
+	    After_Last_Whitespace_Character(bp,
+					    (int)(Get_Start_Of_Data(bp
+								    + field->dcols
+								    - requiredlen,
+								    requiredlen)
+						  - bp));
+	  /* split points now to the first character of the portion of the
+	     line that must be moved to the next line */
+	  datalen = (int)(split - bp);	/* + freelen has to stay on this line   */
+	  freelen = field->dcols - (datalen + freelen);		/* for the next line */
+
+	  if ((result = Insert_String(form, row + 1, split, freelen)) == E_OK)
+	    {
+	      wmove(form->w, row, datalen);
+	      wclrtoeol(form->w);
+	      wmove(form->w, row, 0);
+	      myINSNSTR(form->w, txt, len);
+	      wmove(form->w, row, len);
+	      myINSNSTR(form->w, &myBLANK, 1);
+	      return E_OK;
+	    }
+	}
+      return (result);
+    }
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int Wrapping_Not_Necessary_Or_Wrapping_Ok(
+|                                             FORM * form)
+|
+|   Description   :  If a character has been entered into a field, it may
+|                    be that wrapping has to occur. This routine checks
+|                    whether or not wrapping is required and if so, performs
+|                    the wrapping.
+|
+|   Return Values :  E_OK              - no wrapping required or wrapping
+|                                        was successful
+|                    E_REQUEST_DENIED  -
+|                    E_SYSTEM_ERROR    - some system error
++--------------------------------------------------------------------------*/
+static int
+Wrapping_Not_Necessary_Or_Wrapping_Ok(FORM *form)
+{
+  FIELD *field = form->current;
+  int result = E_REQUEST_DENIED;
+  bool Last_Row = ((field->drows - 1) == form->currow);
+
+  if ((field->opts & O_WRAP) &&	/* wrapping wanted     */
+      (!Single_Line_Field(field)) &&	/* must be multi-line  */
+      (There_Is_No_Room_For_A_Char_In_Line(form)) &&	/* line is full        */
+      (!Last_Row || Growable(field)))	/* there are more lines */
+    {
+      FIELD_CELL *bp;
+      FIELD_CELL *split;
+      int chars_to_be_wrapped;
+      int chars_to_remain_on_line;
+
+      if (Last_Row)
+	{
+	  /* the above logic already ensures, that in this case the field
+	     is growable */
+	  if (!Field_Grown(field, 1))
+	    return E_SYSTEM_ERROR;
+	}
+      bp = Address_Of_Current_Row_In_Buffer(form);
+      Window_To_Buffer(form->w, field);
+      split = After_Last_Whitespace_Character(bp, field->dcols);
+      /* split points to the first character of the sequence to be brought
+         on the next line */
+      chars_to_remain_on_line = (int)(split - bp);
+      chars_to_be_wrapped = field->dcols - chars_to_remain_on_line;
+      if (chars_to_remain_on_line > 0)
+	{
+	  if ((result = Insert_String(form, form->currow + 1, split,
+				      chars_to_be_wrapped)) == E_OK)
+	    {
+	      wmove(form->w, form->currow, chars_to_remain_on_line);
+	      wclrtoeol(form->w);
+	      if (form->curcol >= chars_to_remain_on_line)
+		{
+		  form->currow++;
+		  form->curcol -= chars_to_remain_on_line;
+		}
+	      return E_OK;
+	    }
+	}
+      else
+	return E_OK;
+      if (result != E_OK)
+	{
+	  DeleteChar(form);
+	  Window_To_Buffer(form->w, field);
+	  result = E_REQUEST_DENIED;
+	}
+    }
+  else
+    result = E_OK;		/* wrapping was not necessary */
+  return (result);
+}
+
+/*----------------------------------------------------------------------------
+  Field Editing routines
+  --------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int Field_Editing(
+|                                    int (* const fct) (FORM *),
+|                                    FORM * form)
+|
+|   Description   :  Generic routine for field editing requests. The driver
+|                    routines are only called for editable fields, the
+|                    _WINDOW_MODIFIED flag is set if editing occurred.
+|                    This is somewhat special due to the overload semantics
+|                    of the NEW_LINE and DEL_PREV requests.
+|
+|   Return Values :  Error code from low level drivers.
++--------------------------------------------------------------------------*/
+static int
+Field_Editing(int (*const fct) (FORM *), FORM *form)
+{
+  int res = E_REQUEST_DENIED;
+
+  /* We have to deal here with the specific case of the overloaded
+     behavior of New_Line and Delete_Previous requests.
+     They may end up in navigational requests if we are on the first
+     character in a field. But navigation is also allowed on non-
+     editable fields.
+   */
+  if ((fct == FE_Delete_Previous) &&
+      (form->opts & O_BS_OVERLOAD) &&
+      First_Position_In_Current_Field(form))
+    {
+      res = Inter_Field_Navigation(FN_Previous_Field, form);
+    }
+  else
+    {
+      if (fct == FE_New_Line)
+	{
+	  if ((form->opts & O_NL_OVERLOAD) &&
+	      First_Position_In_Current_Field(form))
+	    {
+	      res = Inter_Field_Navigation(FN_Next_Field, form);
+	    }
+	  else
+	    /* FE_New_Line deals itself with the _WINDOW_MODIFIED flag */
+	    res = fct(form);
+	}
+      else
+	{
+	  /* From now on, everything must be editable */
+	  if (form->current->opts & O_EDIT)
+	    {
+	      res = fct(form);
+	      if (res == E_OK)
+		form->status |= _WINDOW_MODIFIED;
+	    }
+	}
+    }
+  return res;
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FE_New_Line(FORM * form)
+|
+|   Description   :  Perform a new line request. This is rather complex
+|                    compared to other routines in this code due to the
+|                    rather difficult to understand description in the
+|                    manuals.
+|
+|   Return Values :  E_OK               - success
+|                    E_REQUEST_DENIED   - new line not allowed
+|                    E_SYSTEM_ERROR     - system error
++--------------------------------------------------------------------------*/
+static int
+FE_New_Line(FORM *form)
+{
+  FIELD *field = form->current;
+  FIELD_CELL *bp, *t;
+  bool Last_Row = ((field->drows - 1) == form->currow);
+
+  T((T_CALLED("FE_New_Line(%p)"), form));
+  if (form->status & _OVLMODE)
+    {
+      if (Last_Row &&
+	  (!(Growable(field) && !Single_Line_Field(field))))
+	{
+	  if (!(form->opts & O_NL_OVERLOAD))
+	    returnCode(E_REQUEST_DENIED);
+	  wmove(form->w, form->currow, form->curcol);
+	  wclrtoeol(form->w);
+	  /* we have to set this here, although it is also
+	     handled in the generic routine. The reason is,
+	     that FN_Next_Field may fail, but the form is
+	     definitively changed */
+	  form->status |= _WINDOW_MODIFIED;
+	  returnCode(Inter_Field_Navigation(FN_Next_Field, form));
+	}
+      else
+	{
+	  if (Last_Row && !Field_Grown(field, 1))
+	    {
+	      /* N.B.: due to the logic in the 'if', LastRow==TRUE
+	         means here that the field is growable and not
+	         a single-line field */
+	      returnCode(E_SYSTEM_ERROR);
+	    }
+	  wmove(form->w, form->currow, form->curcol);
+	  wclrtoeol(form->w);
+	  form->currow++;
+	  form->curcol = 0;
+	  form->status |= _WINDOW_MODIFIED;
+	  returnCode(E_OK);
+	}
+    }
+  else
+    {
+      /* Insert Mode */
+      if (Last_Row &&
+	  !(Growable(field) && !Single_Line_Field(field)))
+	{
+	  if (!(form->opts & O_NL_OVERLOAD))
+	    returnCode(E_REQUEST_DENIED);
+	  returnCode(Inter_Field_Navigation(FN_Next_Field, form));
+	}
+      else
+	{
+	  bool May_Do_It = !Last_Row && Is_There_Room_For_A_Line(form);
+
+	  if (!(May_Do_It || Growable(field)))
+	    returnCode(E_REQUEST_DENIED);
+	  if (!May_Do_It && !Field_Grown(field, 1))
+	    returnCode(E_SYSTEM_ERROR);
+
+	  bp = Address_Of_Current_Position_In_Buffer(form);
+	  t = After_End_Of_Data(bp, field->dcols - form->curcol);
+	  wmove(form->w, form->currow, form->curcol);
+	  wclrtoeol(form->w);
+	  form->currow++;
+	  form->curcol = 0;
+	  wmove(form->w, form->currow, form->curcol);
+	  winsertln(form->w);
+	  myADDNSTR(form->w, bp, (int)(t - bp));
+	  form->status |= _WINDOW_MODIFIED;
+	  returnCode(E_OK);
+	}
+    }
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FE_Insert_Character(FORM * form)
+|
+|   Description   :  Insert blank character at the cursor position
+|
+|   Return Values :  E_OK
+|                    E_REQUEST_DENIED
++--------------------------------------------------------------------------*/
+static int
+FE_Insert_Character(FORM *form)
+{
+  FIELD *field = form->current;
+  int result = E_REQUEST_DENIED;
+
+  T((T_CALLED("FE_Insert_Character(%p)"), form));
+  if (Check_Char(field->type, (int)C_BLANK, (TypeArgument *)(field->arg)))
+    {
+      bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
+
+      if (There_Is_Room ||
+	  ((Single_Line_Field(field) && Growable(field))))
+	{
+	  if (!There_Is_Room && !Field_Grown(field, 1))
+	    result = E_SYSTEM_ERROR;
+	  else
+	    {
+	      winsch(form->w, (chtype)C_BLANK);
+	      result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form);
+	    }
+	}
+    }
+  returnCode(result);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FE_Insert_Line(FORM * form)
+|
+|   Description   :  Insert a blank line at the cursor position
+|
+|   Return Values :  E_OK               - success
+|                    E_REQUEST_DENIED   - line can not be inserted
++--------------------------------------------------------------------------*/
+static int
+FE_Insert_Line(FORM *form)
+{
+  FIELD *field = form->current;
+  int result = E_REQUEST_DENIED;
+
+  T((T_CALLED("FE_Insert_Line(%p)"), form));
+  if (Check_Char(field->type, (int)C_BLANK, (TypeArgument *)(field->arg)))
+    {
+      bool Maybe_Done = (form->currow != (field->drows - 1)) &&
+      Is_There_Room_For_A_Line(form);
+
+      if (!Single_Line_Field(field) &&
+	  (Maybe_Done || Growable(field)))
+	{
+	  if (!Maybe_Done && !Field_Grown(field, 1))
+	    result = E_SYSTEM_ERROR;
+	  else
+	    {
+	      form->curcol = 0;
+	      winsertln(form->w);
+	      result = E_OK;
+	    }
+	}
+    }
+  returnCode(result);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FE_Delete_Character(FORM * form)
+|
+|   Description   :  Delete character at the cursor position
+|
+|   Return Values :  E_OK    - success
++--------------------------------------------------------------------------*/
+static int
+FE_Delete_Character(FORM *form)
+{
+  T((T_CALLED("FE_Delete_Character(%p)"), form));
+  DeleteChar(form);
+  returnCode(E_OK);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FE_Delete_Previous(FORM * form)
+|
+|   Description   :  Delete character before cursor. Again this is a rather
+|                    difficult piece compared to others due to the overloading
+|                    semantics of backspace.
+|                    N.B.: The case of overloaded BS on first field position
+|                          is already handled in the generic routine.
+|
+|   Return Values :  E_OK                - success
+|                    E_REQUEST_DENIED    - Character can't be deleted
++--------------------------------------------------------------------------*/
+static int
+FE_Delete_Previous(FORM *form)
+{
+  FIELD *field = form->current;
+
+  T((T_CALLED("FE_Delete_Previous(%p)"), form));
+  if (First_Position_In_Current_Field(form))
+    returnCode(E_REQUEST_DENIED);
+
+  if ((--(form->curcol)) < 0)
+    {
+      FIELD_CELL *this_line, *prev_line, *prev_end, *this_end;
+      int this_row = form->currow;
+
+      form->curcol++;
+      if (form->status & _OVLMODE)
+	returnCode(E_REQUEST_DENIED);
+
+      prev_line = Address_Of_Row_In_Buffer(field, (form->currow - 1));
+      this_line = Address_Of_Row_In_Buffer(field, (form->currow));
+      Synchronize_Buffer(form);
+      prev_end = After_End_Of_Data(prev_line, field->dcols);
+      this_end = After_End_Of_Data(this_line, field->dcols);
+      if ((int)(this_end - this_line) >
+	  (field->cols - (int)(prev_end - prev_line)))
+	returnCode(E_REQUEST_DENIED);
+      wmove(form->w, form->currow, form->curcol);
+      wdeleteln(form->w);
+      Adjust_Cursor_Position(form, prev_end);
+      /*
+       * If we did not really move to the previous line, help the user a
+       * little.  It is however a little inconsistent.  Normally, when
+       * backspacing around the point where text wraps to a new line in a
+       * multi-line form, we absorb one keystroke for the wrapping point.  That
+       * is consistent with SVr4 forms.  However, SVr4 does not allow typing
+       * into the last column of the field, and requires the user to enter a
+       * newline to move to the next line.  Therefore it can consistently eat
+       * that keystroke.  Since ncurses allows the last column, it wraps
+       * automatically (given the proper options).  But we cannot eat the
+       * keystroke to back over the wrapping point, since that would put the
+       * cursor past the end of the form field.  In this case, just delete the
+       * character at the end of the field.
+       */
+      if (form->currow == this_row && this_row > 0)
+	{
+	  form->currow -= 1;
+	  form->curcol = field->dcols - 1;
+	  DeleteChar(form);
+	}
+      else
+	{
+	  wmove(form->w, form->currow, form->curcol);
+	  myADDNSTR(form->w, this_line, (int)(this_end - this_line));
+	}
+    }
+  else
+    {
+      DeleteChar(form);
+    }
+  returnCode(E_OK);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FE_Delete_Line(FORM * form)
+|
+|   Description   :  Delete line at cursor position.
+|
+|   Return Values :  E_OK  - success
++--------------------------------------------------------------------------*/
+static int
+FE_Delete_Line(FORM *form)
+{
+  T((T_CALLED("FE_Delete_Line(%p)"), form));
+  form->curcol = 0;
+  wdeleteln(form->w);
+  returnCode(E_OK);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FE_Delete_Word(FORM * form)
+|
+|   Description   :  Delete word at cursor position
+|
+|   Return Values :  E_OK               - success
+|                    E_REQUEST_DENIED   - failure
++--------------------------------------------------------------------------*/
+static int
+FE_Delete_Word(FORM *form)
+{
+  FIELD *field = form->current;
+  FIELD_CELL *bp = Address_Of_Current_Row_In_Buffer(form);
+  FIELD_CELL *ep = bp + field->dcols;
+  FIELD_CELL *cp = bp + form->curcol;
+  FIELD_CELL *s;
+
+  T((T_CALLED("FE_Delete_Word(%p)"), form));
+  Synchronize_Buffer(form);
+  if (ISBLANK(*cp))
+    returnCode(E_REQUEST_DENIED);	/* not in word */
+
+  /* move cursor to begin of word and erase to end of screen-line */
+  Adjust_Cursor_Position(form,
+			 After_Last_Whitespace_Character(bp, form->curcol));
+  wmove(form->w, form->currow, form->curcol);
+  wclrtoeol(form->w);
+
+  /* skip over word in buffer */
+  s = Get_First_Whitespace_Character(cp, (int)(ep - cp));
+  /* to begin of next word    */
+  s = Get_Start_Of_Data(s, (int)(ep - s));
+  if ((s != cp) && !ISBLANK(*s))
+    {
+      /* copy remaining line to window */
+      myADDNSTR(form->w, s, (int)(s - After_End_Of_Data(s, (int)(ep - s))));
+    }
+  returnCode(E_OK);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FE_Clear_To_End_Of_Line(FORM * form)
+|
+|   Description   :  Clear to end of current line.
+|
+|   Return Values :  E_OK   - success
++--------------------------------------------------------------------------*/
+static int
+FE_Clear_To_End_Of_Line(FORM *form)
+{
+  T((T_CALLED("FE_Clear_To_End_Of_Line(%p)"), form));
+  wmove(form->w, form->currow, form->curcol);
+  wclrtoeol(form->w);
+  returnCode(E_OK);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FE_Clear_To_End_Of_Field(FORM * form)
+|
+|   Description   :  Clear to end of field.
+|
+|   Return Values :  E_OK   - success
++--------------------------------------------------------------------------*/
+static int
+FE_Clear_To_End_Of_Field(FORM *form)
+{
+  T((T_CALLED("FE_Clear_To_End_Of_Field(%p)"), form));
+  wmove(form->w, form->currow, form->curcol);
+  wclrtobot(form->w);
+  returnCode(E_OK);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FE_Clear_Field(FORM * form)
+|
+|   Description   :  Clear entire field.
+|
+|   Return Values :  E_OK   - success
++--------------------------------------------------------------------------*/
+static int
+FE_Clear_Field(FORM *form)
+{
+  T((T_CALLED("FE_Clear_Field(%p)"), form));
+  form->currow = form->curcol = 0;
+  werase(form->w);
+  returnCode(E_OK);
+}
+/*----------------------------------------------------------------------------
+  END of Field Editing routines
+  --------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------
+  Edit Mode routines
+  --------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int EM_Overlay_Mode(FORM * form)
+|
+|   Description   :  Switch to overlay mode.
+|
+|   Return Values :  E_OK   - success
++--------------------------------------------------------------------------*/
+static int
+EM_Overlay_Mode(FORM *form)
+{
+  T((T_CALLED("EM_Overlay_Mode(%p)"), form));
+  form->status |= _OVLMODE;
+  returnCode(E_OK);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int EM_Insert_Mode(FORM * form)
+|
+|   Description   :  Switch to insert mode
+|
+|   Return Values :  E_OK   - success
++--------------------------------------------------------------------------*/
+static int
+EM_Insert_Mode(FORM *form)
+{
+  T((T_CALLED("EM_Insert_Mode(%p)"), form));
+  form->status &= ~_OVLMODE;
+  returnCode(E_OK);
+}
+
+/*----------------------------------------------------------------------------
+  END of Edit Mode routines
+  --------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------
+  Helper routines for Choice Requests
+  --------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static bool Next_Choice(
+|                                            FIELDTYPE * typ,
+|                                            FIELD * field,
+|                                            TypeArgument *argp)
+|
+|   Description   :  Get the next field choice. For linked types this is
+|                    done recursively.
+|
+|   Return Values :  TRUE    - next choice successfully retrieved
+|                    FALSE   - couldn't retrieve next choice
++--------------------------------------------------------------------------*/
+static bool
+Next_Choice(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
+{
+  if (!typ || !(typ->status & _HAS_CHOICE))
+    return FALSE;
+
+  if (typ->status & _LINKED_TYPE)
+    {
+      assert(argp);
+      return (
+	       Next_Choice(typ->left, field, argp->left) ||
+	       Next_Choice(typ->right, field, argp->right));
+    }
+  else
+    {
+      assert(typ->next);
+      return typ->next(field, (void *)argp);
+    }
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static bool Previous_Choice(
+|                                                FIELDTYPE * typ,
+|                                                FIELD * field,
+|                                                TypeArgument *argp)
+|
+|   Description   :  Get the previous field choice. For linked types this
+|                    is done recursively.
+|
+|   Return Values :  TRUE    - previous choice successfully retrieved
+|                    FALSE   - couldn't retrieve previous choice
++--------------------------------------------------------------------------*/
+static bool
+Previous_Choice(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
+{
+  if (!typ || !(typ->status & _HAS_CHOICE))
+    return FALSE;
+
+  if (typ->status & _LINKED_TYPE)
+    {
+      assert(argp);
+      return (
+	       Previous_Choice(typ->left, field, argp->left) ||
+	       Previous_Choice(typ->right, field, argp->right));
+    }
+  else
+    {
+      assert(typ->prev);
+      return typ->prev(field, (void *)argp);
+    }
+}
+/*----------------------------------------------------------------------------
+  End of Helper routines for Choice Requests
+  --------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------
+  Routines for Choice Requests
+  --------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int CR_Next_Choice(FORM * form)
+|
+|   Description   :  Get the next field choice.
+|
+|   Return Values :  E_OK              - success
+|                    E_REQUEST_DENIED  - next choice couldn't be retrieved
++--------------------------------------------------------------------------*/
+static int
+CR_Next_Choice(FORM *form)
+{
+  FIELD *field = form->current;
+
+  T((T_CALLED("CR_Next_Choice(%p)"), form));
+  Synchronize_Buffer(form);
+  returnCode((Next_Choice(field->type, field, (TypeArgument *)(field->arg)))
+	     ? E_OK
+	     : E_REQUEST_DENIED);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int CR_Previous_Choice(FORM * form)
+|
+|   Description   :  Get the previous field choice.
+|
+|   Return Values :  E_OK              - success
+|                    E_REQUEST_DENIED  - prev. choice couldn't be retrieved
++--------------------------------------------------------------------------*/
+static int
+CR_Previous_Choice(FORM *form)
+{
+  FIELD *field = form->current;
+
+  T((T_CALLED("CR_Previous_Choice(%p)"), form));
+  Synchronize_Buffer(form);
+  returnCode((Previous_Choice(field->type, field, (TypeArgument *)(field->arg)))
+	     ? E_OK
+	     : E_REQUEST_DENIED);
+}
+/*----------------------------------------------------------------------------
+  End of Routines for Choice Requests
+  --------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------
+  Helper routines for Field Validations.
+  --------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static bool Check_Field(
+|                                            FIELDTYPE * typ,
+|                                            FIELD * field,
+|                                            TypeArgument * argp)
+|
+|   Description   :  Check the field according to its fieldtype and its
+|                    actual arguments. For linked fieldtypes this is done
+|                    recursively.
+|
+|   Return Values :  TRUE       - field is valid
+|                    FALSE      - field is invalid.
++--------------------------------------------------------------------------*/
+static bool
+Check_Field(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
+{
+  if (typ)
+    {
+      if (field->opts & O_NULLOK)
+	{
+	  FIELD_CELL *bp = field->buf;
+
+	  assert(bp);
+	  while (ISBLANK(*bp))
+	    {
+	      bp++;
+	    }
+	  if (CharOf(*bp) == 0)
+	    return TRUE;
+	}
+
+      if (typ->status & _LINKED_TYPE)
+	{
+	  assert(argp);
+	  return (
+		   Check_Field(typ->left, field, argp->left) ||
+		   Check_Field(typ->right, field, argp->right));
+	}
+      else
+	{
+	  if (typ->fcheck)
+	    return typ->fcheck(field, (void *)argp);
+	}
+    }
+  return TRUE;
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  bool _nc_Internal_Validation(FORM * form )
+|
+|   Description   :  Validate the current field of the form.
+|
+|   Return Values :  TRUE  - field is valid
+|                    FALSE - field is invalid
++--------------------------------------------------------------------------*/
+NCURSES_EXPORT(bool)
+_nc_Internal_Validation(FORM *form)
+{
+  FIELD *field;
+
+  field = form->current;
+
+  Synchronize_Buffer(form);
+  if ((form->status & _FCHECK_REQUIRED) ||
+      (!(field->opts & O_PASSOK)))
+    {
+      if (!Check_Field(field->type, field, (TypeArgument *)(field->arg)))
+	return FALSE;
+      form->status &= ~_FCHECK_REQUIRED;
+      field->status |= _CHANGED;
+      Synchronize_Linked_Fields(field);
+    }
+  return TRUE;
+}
+/*----------------------------------------------------------------------------
+  End of Helper routines for Field Validations.
+  --------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------
+  Routines for Field Validation.
+  --------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FV_Validation(FORM * form)
+|
+|   Description   :  Validate the current field of the form.
+|
+|   Return Values :  E_OK             - field valid
+|                    E_INVALID_FIELD  - field not valid
++--------------------------------------------------------------------------*/
+static int
+FV_Validation(FORM *form)
+{
+  T((T_CALLED("FV_Validation(%p)"), form));
+  if (_nc_Internal_Validation(form))
+    returnCode(E_OK);
+  else
+    returnCode(E_INVALID_FIELD);
+}
+/*----------------------------------------------------------------------------
+  End of routines for Field Validation.
+  --------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------
+  Helper routines for Inter-Field Navigation
+  --------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static FIELD *Next_Field_On_Page(FIELD * field)
+|
+|   Description   :  Get the next field after the given field on the current
+|                    page. The order of fields is the one defined by the
+|                    fields array. Only visible and active fields are
+|                    counted.
+|
+|   Return Values :  Pointer to the next field.
++--------------------------------------------------------------------------*/
+NCURSES_INLINE static FIELD *
+Next_Field_On_Page(FIELD *field)
+{
+  FORM *form = field->form;
+  FIELD **field_on_page = &form->field[field->index];
+  FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
+  FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
+
+  do
+    {
+      field_on_page =
+	(field_on_page == last_on_page) ? first_on_page : field_on_page + 1;
+      if (Field_Is_Selectable(*field_on_page))
+	break;
+    }
+  while (field != (*field_on_page));
+  return (*field_on_page);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  FIELD* _nc_First_Active_Field(FORM * form)
+|
+|   Description   :  Get the first active field on the current page,
+|                    if there are such. If there are none, get the first
+|                    visible field on the page. If there are also none,
+|                    we return the first field on page and hope the best.
+|
+|   Return Values :  Pointer to calculated field.
++--------------------------------------------------------------------------*/
+NCURSES_EXPORT(FIELD *)
+_nc_First_Active_Field(FORM *form)
+{
+  FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
+  FIELD *proposed = Next_Field_On_Page(*last_on_page);
+
+  if (proposed == *last_on_page)
+    {
+      /* there might be the special situation, where there is no
+         active and visible field on the current page. We then select
+         the first visible field on this readonly page
+       */
+      if (Field_Is_Not_Selectable(proposed))
+	{
+	  FIELD **field = &form->field[proposed->index];
+	  FIELD **first = &form->field[form->page[form->curpage].pmin];
+
+	  do
+	    {
+	      field = (field == last_on_page) ? first : field + 1;
+	      if (((*field)->opts & O_VISIBLE))
+		break;
+	    }
+	  while (proposed != (*field));
+
+	  proposed = *field;
+
+	  if ((proposed == *last_on_page) && !(proposed->opts & O_VISIBLE))
+	    {
+	      /* This means, there is also no visible field on the page.
+	         So we propose the first one and hope the very best...
+	         Some very clever user has designed a readonly and invisible
+	         page on this form.
+	       */
+	      proposed = *first;
+	    }
+	}
+    }
+  return (proposed);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static FIELD *Previous_Field_On_Page(FIELD * field)
+|
+|   Description   :  Get the previous field before the given field on the
+|                    current page. The order of fields is the one defined by
+|                    the fields array. Only visible and active fields are
+|                    counted.
+|
+|   Return Values :  Pointer to the previous field.
++--------------------------------------------------------------------------*/
+NCURSES_INLINE static FIELD *
+Previous_Field_On_Page(FIELD *field)
+{
+  FORM *form = field->form;
+  FIELD **field_on_page = &form->field[field->index];
+  FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
+  FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
+
+  do
+    {
+      field_on_page =
+	(field_on_page == first_on_page) ? last_on_page : field_on_page - 1;
+      if (Field_Is_Selectable(*field_on_page))
+	break;
+    }
+  while (field != (*field_on_page));
+
+  return (*field_on_page);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static FIELD *Sorted_Next_Field(FIELD * field)
+|
+|   Description   :  Get the next field after the given field on the current
+|                    page. The order of fields is the one defined by the
+|                    (row,column) geometry, rows are major.
+|
+|   Return Values :  Pointer to the next field.
++--------------------------------------------------------------------------*/
+NCURSES_INLINE static FIELD *
+Sorted_Next_Field(FIELD *field)
+{
+  FIELD *field_on_page = field;
+
+  do
+    {
+      field_on_page = field_on_page->snext;
+      if (Field_Is_Selectable(field_on_page))
+	break;
+    }
+  while (field_on_page != field);
+
+  return (field_on_page);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static FIELD *Sorted_Previous_Field(FIELD * field)
+|
+|   Description   :  Get the previous field before the given field on the
+|                    current page. The order of fields is the one defined
+|                    by the (row,column) geometry, rows are major.
+|
+|   Return Values :  Pointer to the previous field.
++--------------------------------------------------------------------------*/
+NCURSES_INLINE static FIELD *
+Sorted_Previous_Field(FIELD *field)
+{
+  FIELD *field_on_page = field;
+
+  do
+    {
+      field_on_page = field_on_page->sprev;
+      if (Field_Is_Selectable(field_on_page))
+	break;
+    }
+  while (field_on_page != field);
+
+  return (field_on_page);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static FIELD *Left_Neighbor_Field(FIELD * field)
+|
+|   Description   :  Get the left neighbor of the field on the same line
+|                    and the same page. Cycles through the line.
+|
+|   Return Values :  Pointer to left neighbor field.
++--------------------------------------------------------------------------*/
+NCURSES_INLINE static FIELD *
+Left_Neighbor_Field(FIELD *field)
+{
+  FIELD *field_on_page = field;
+
+  /* For a field that has really a left neighbor, the while clause
+     immediately fails and the loop is left, positioned at the right
+     neighbor. Otherwise we cycle backwards through the sorted field list
+     until we enter the same line (from the right end).
+   */
+  do
+    {
+      field_on_page = Sorted_Previous_Field(field_on_page);
+    }
+  while (field_on_page->frow != field->frow);
+
+  return (field_on_page);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static FIELD *Right_Neighbor_Field(FIELD * field)
+|
+|   Description   :  Get the right neighbor of the field on the same line
+|                    and the same page.
+|
+|   Return Values :  Pointer to right neighbor field.
++--------------------------------------------------------------------------*/
+NCURSES_INLINE static FIELD *
+Right_Neighbor_Field(FIELD *field)
+{
+  FIELD *field_on_page = field;
+
+  /* See the comments on Left_Neighbor_Field to understand how it works */
+  do
+    {
+      field_on_page = Sorted_Next_Field(field_on_page);
+    }
+  while (field_on_page->frow != field->frow);
+
+  return (field_on_page);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static FIELD *Upper_Neighbor_Field(FIELD * field)
+|
+|   Description   :  Because of the row-major nature of sorting the fields,
+|                    it is more difficult to define whats the upper neighbor
+|                    field really means. We define that it must be on a
+|                    'previous' line (cyclic order!) and is the rightmost
+|                    field laying on the left side of the given field. If
+|                    this set is empty, we take the first field on the line.
+|
+|   Return Values :  Pointer to the upper neighbor field.
++--------------------------------------------------------------------------*/
+static FIELD *
+Upper_Neighbor_Field(FIELD *field)
+{
+  FIELD *field_on_page = field;
+  int frow = field->frow;
+  int fcol = field->fcol;
+
+  /* Walk back to the 'previous' line. The second term in the while clause
+     just guarantees that we stop if we cycled through the line because
+     there might be no 'previous' line if the page has just one line.
+   */
+  do
+    {
+      field_on_page = Sorted_Previous_Field(field_on_page);
+    }
+  while (field_on_page->frow == frow && field_on_page->fcol != fcol);
+
+  if (field_on_page->frow != frow)
+    {
+      /* We really found a 'previous' line. We are positioned at the
+         rightmost field on this line */
+      frow = field_on_page->frow;
+
+      /* We walk to the left as long as we are really right of the
+         field. */
+      while (field_on_page->frow == frow && field_on_page->fcol > fcol)
+	field_on_page = Sorted_Previous_Field(field_on_page);
+
+      /* If we wrapped, just go to the right which is the first field on
+         the row */
+      if (field_on_page->frow != frow)
+	field_on_page = Sorted_Next_Field(field_on_page);
+    }
+
+  return (field_on_page);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static FIELD *Down_Neighbor_Field(FIELD * field)
+|
+|   Description   :  Because of the row-major nature of sorting the fields,
+|                    its more difficult to define whats the down neighbor
+|                    field really means. We define that it must be on a
+|                    'next' line (cyclic order!) and is the leftmost
+|                    field laying on the right side of the given field. If
+|                    this set is empty, we take the last field on the line.
+|
+|   Return Values :  Pointer to the upper neighbor field.
++--------------------------------------------------------------------------*/
+static FIELD *
+Down_Neighbor_Field(FIELD *field)
+{
+  FIELD *field_on_page = field;
+  int frow = field->frow;
+  int fcol = field->fcol;
+
+  /* Walk forward to the 'next' line. The second term in the while clause
+     just guarantees that we stop if we cycled through the line because
+     there might be no 'next' line if the page has just one line.
+   */
+  do
+    {
+      field_on_page = Sorted_Next_Field(field_on_page);
+    }
+  while (field_on_page->frow == frow && field_on_page->fcol != fcol);
+
+  if (field_on_page->frow != frow)
+    {
+      /* We really found a 'next' line. We are positioned at the rightmost
+         field on this line */
+      frow = field_on_page->frow;
+
+      /* We walk to the right as long as we are really left of the
+         field. */
+      while (field_on_page->frow == frow && field_on_page->fcol < fcol)
+	field_on_page = Sorted_Next_Field(field_on_page);
+
+      /* If we wrapped, just go to the left which is the last field on
+         the row */
+      if (field_on_page->frow != frow)
+	field_on_page = Sorted_Previous_Field(field_on_page);
+    }
+
+  return (field_on_page);
+}
+
+/*----------------------------------------------------------------------------
+  Inter-Field Navigation routines
+  --------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int Inter_Field_Navigation(
+|                                           int (* const fct) (FORM *),
+|                                           FORM * form)
+|
+|   Description   :  Generic behavior for changing the current field, the
+|                    field is left and a new field is entered. So the field
+|                    must be validated and the field init/term hooks must
+|                    be called.
+|
+|   Return Values :  E_OK                - success
+|                    E_INVALID_FIELD     - field is invalid
+|                    some other          - error from subordinate call
++--------------------------------------------------------------------------*/
+static int
+Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form)
+{
+  int res;
+
+  if (!_nc_Internal_Validation(form))
+    res = E_INVALID_FIELD;
+  else
+    {
+      Call_Hook(form, fieldterm);
+      res = fct(form);
+      Call_Hook(form, fieldinit);
+    }
+  return res;
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FN_Next_Field(FORM * form)
+|
+|   Description   :  Move to the next field on the current page of the form
+|
+|   Return Values :  E_OK                 - success
+|                    != E_OK              - error from subordinate call
++--------------------------------------------------------------------------*/
+static int
+FN_Next_Field(FORM *form)
+{
+  T((T_CALLED("FN_Next_Field(%p)"), form));
+  returnCode(_nc_Set_Current_Field(form,
+				   Next_Field_On_Page(form->current)));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FN_Previous_Field(FORM * form)
+|
+|   Description   :  Move to the previous field on the current page of the
+|                    form
+|
+|   Return Values :  E_OK                 - success
+|                    != E_OK              - error from subordinate call
++--------------------------------------------------------------------------*/
+static int
+FN_Previous_Field(FORM *form)
+{
+  T((T_CALLED("FN_Previous_Field(%p)"), form));
+  returnCode(_nc_Set_Current_Field(form,
+				   Previous_Field_On_Page(form->current)));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FN_First_Field(FORM * form)
+|
+|   Description   :  Move to the first field on the current page of the form
+|
+|   Return Values :  E_OK                 - success
+|                    != E_OK              - error from subordinate call
++--------------------------------------------------------------------------*/
+static int
+FN_First_Field(FORM *form)
+{
+  T((T_CALLED("FN_First_Field(%p)"), form));
+  returnCode(_nc_Set_Current_Field(form,
+				   Next_Field_On_Page(form->field[form->page[form->curpage].pmax])));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FN_Last_Field(FORM * form)
+|
+|   Description   :  Move to the last field on the current page of the form
+|
+|   Return Values :  E_OK                 - success
+|                    != E_OK              - error from subordinate call
++--------------------------------------------------------------------------*/
+static int
+FN_Last_Field(FORM *form)
+{
+  T((T_CALLED("FN_Last_Field(%p)"), form));
+  returnCode(
+	      _nc_Set_Current_Field(form,
+				    Previous_Field_On_Page(form->field[form->page[form->curpage].pmin])));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FN_Sorted_Next_Field(FORM * form)
+|
+|   Description   :  Move to the sorted next field on the current page
+|                    of the form.
+|
+|   Return Values :  E_OK            - success
+|                    != E_OK         - error from subordinate call
++--------------------------------------------------------------------------*/
+static int
+FN_Sorted_Next_Field(FORM *form)
+{
+  T((T_CALLED("FN_Sorted_Next_Field(%p)"), form));
+  returnCode(_nc_Set_Current_Field(form,
+				   Sorted_Next_Field(form->current)));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FN_Sorted_Previous_Field(FORM * form)
+|
+|   Description   :  Move to the sorted previous field on the current page
+|                    of the form.
+|
+|   Return Values :  E_OK            - success
+|                    != E_OK         - error from subordinate call
++--------------------------------------------------------------------------*/
+static int
+FN_Sorted_Previous_Field(FORM *form)
+{
+  T((T_CALLED("FN_Sorted_Previous_Field(%p)"), form));
+  returnCode(_nc_Set_Current_Field(form,
+				   Sorted_Previous_Field(form->current)));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FN_Sorted_First_Field(FORM * form)
+|
+|   Description   :  Move to the sorted first field on the current page
+|                    of the form.
+|
+|   Return Values :  E_OK            - success
+|                    != E_OK         - error from subordinate call
++--------------------------------------------------------------------------*/
+static int
+FN_Sorted_First_Field(FORM *form)
+{
+  T((T_CALLED("FN_Sorted_First_Field(%p)"), form));
+  returnCode(_nc_Set_Current_Field(form,
+				   Sorted_Next_Field(form->field[form->page[form->curpage].smax])));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FN_Sorted_Last_Field(FORM * form)
+|
+|   Description   :  Move to the sorted last field on the current page
+|                    of the form.
+|
+|   Return Values :  E_OK            - success
+|                    != E_OK         - error from subordinate call
++--------------------------------------------------------------------------*/
+static int
+FN_Sorted_Last_Field(FORM *form)
+{
+  T((T_CALLED("FN_Sorted_Last_Field(%p)"), form));
+  returnCode(_nc_Set_Current_Field(form,
+				   Sorted_Previous_Field(form->field[form->page[form->curpage].smin])));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FN_Left_Field(FORM * form)
+|
+|   Description   :  Get the field on the left of the current field on the
+|                    same line and the same page. Cycles through the line.
+|
+|   Return Values :  E_OK            - success
+|                    != E_OK         - error from subordinate call
++--------------------------------------------------------------------------*/
+static int
+FN_Left_Field(FORM *form)
+{
+  T((T_CALLED("FN_Left_Field(%p)"), form));
+  returnCode(_nc_Set_Current_Field(form,
+				   Left_Neighbor_Field(form->current)));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FN_Right_Field(FORM * form)
+|
+|   Description   :  Get the field on the right of the current field on the
+|                    same line and the same page. Cycles through the line.
+|
+|   Return Values :  E_OK            - success
+|                    != E_OK         - error from subordinate call
++--------------------------------------------------------------------------*/
+static int
+FN_Right_Field(FORM *form)
+{
+  T((T_CALLED("FN_Right_Field(%p)"), form));
+  returnCode(_nc_Set_Current_Field(form,
+				   Right_Neighbor_Field(form->current)));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FN_Up_Field(FORM * form)
+|
+|   Description   :  Get the upper neighbor of the current field. This
+|                    cycles through the page. See the comments of the
+|                    Upper_Neighbor_Field function to understand how
+|                    'upper' is defined.
+|
+|   Return Values :  E_OK            - success
+|                    != E_OK         - error from subordinate call
++--------------------------------------------------------------------------*/
+static int
+FN_Up_Field(FORM *form)
+{
+  T((T_CALLED("FN_Up_Field(%p)"), form));
+  returnCode(_nc_Set_Current_Field(form,
+				   Upper_Neighbor_Field(form->current)));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int FN_Down_Field(FORM * form)
+|
+|   Description   :  Get the down neighbor of the current field. This
+|                    cycles through the page. See the comments of the
+|                    Down_Neighbor_Field function to understand how
+|                    'down' is defined.
+|
+|   Return Values :  E_OK            - success
+|                    != E_OK         - error from subordinate call
++--------------------------------------------------------------------------*/
+static int
+FN_Down_Field(FORM *form)
+{
+  T((T_CALLED("FN_Down_Field(%p)"), form));
+  returnCode(_nc_Set_Current_Field(form,
+				   Down_Neighbor_Field(form->current)));
+}
+/*----------------------------------------------------------------------------
+  END of Field Navigation routines
+  --------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------
+  Helper routines for Page Navigation
+  --------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  int _nc_Set_Form_Page(FORM * form,
+|                                          int page,
+|                                          FIELD * field)
+|
+|   Description   :  Make the given page number the current page and make
+|                    the given field the current field on the page. If
+|                    for the field NULL is given, make the first field on
+|                    the page the current field. The routine acts only
+|                    if the requested page is not the current page.
+|
+|   Return Values :  E_OK                - success
+|                    != E_OK             - error from subordinate call
+|                    E_BAD_ARGUMENT      - invalid field pointer
+|                    E_SYSTEM_ERROR      - some severe basic error
++--------------------------------------------------------------------------*/
+NCURSES_EXPORT(int)
+_nc_Set_Form_Page(FORM *form, int page, FIELD *field)
+{
+  int res = E_OK;
+
+  if ((form->curpage != page))
+    {
+      FIELD *last_field, *field_on_page;
+
+      werase(Get_Form_Window(form));
+      form->curpage = page;
+      last_field = field_on_page = form->field[form->page[page].smin];
+      do
+	{
+	  if (field_on_page->opts & O_VISIBLE)
+	    if ((res = Display_Field(field_on_page)) != E_OK)
+	      return (res);
+	  field_on_page = field_on_page->snext;
+	}
+      while (field_on_page != last_field);
+
+      if (field)
+	res = _nc_Set_Current_Field(form, field);
+      else
+	/* N.B.: we don't encapsulate this by Inter_Field_Navigation(),
+	   because this is already executed in a page navigation
+	   context that contains field navigation
+	 */
+	res = FN_First_Field(form);
+    }
+  return (res);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int Next_Page_Number(const FORM * form)
+|
+|   Description   :  Calculate the page number following the current page
+|                    number. This cycles if the highest page number is
+|                    reached.
+|
+|   Return Values :  The next page number
++--------------------------------------------------------------------------*/
+NCURSES_INLINE static int
+Next_Page_Number(const FORM *form)
+{
+  return (form->curpage + 1) % form->maxpage;
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int Previous_Page_Number(const FORM * form)
+|
+|   Description   :  Calculate the page number before the current page
+|                    number. This cycles if the first page number is
+|                    reached.
+|
+|   Return Values :  The previous page number
++--------------------------------------------------------------------------*/
+NCURSES_INLINE static int
+Previous_Page_Number(const FORM *form)
+{
+  return (form->curpage != 0 ? form->curpage - 1 : form->maxpage - 1);
+}
+
+/*----------------------------------------------------------------------------
+  Page Navigation routines
+  --------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int Page_Navigation(
+|                                               int (* const fct) (FORM *),
+|                                               FORM * form)
+|
+|   Description   :  Generic behavior for changing a page. This means
+|                    that the field is left and a new field is entered.
+|                    So the field must be validated and the field init/term
+|                    hooks must be called. Because also the page is changed,
+|                    the forms init/term hooks must be called also.
+|
+|   Return Values :  E_OK                - success
+|                    E_INVALID_FIELD     - field is invalid
+|                    some other          - error from subordinate call
++--------------------------------------------------------------------------*/
+static int
+Page_Navigation(int (*const fct) (FORM *), FORM *form)
+{
+  int res;
+
+  if (!_nc_Internal_Validation(form))
+    res = E_INVALID_FIELD;
+  else
+    {
+      Call_Hook(form, fieldterm);
+      Call_Hook(form, formterm);
+      res = fct(form);
+      Call_Hook(form, forminit);
+      Call_Hook(form, fieldinit);
+    }
+  return res;
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int PN_Next_Page(FORM * form)
+|
+|   Description   :  Move to the next page of the form
+|
+|   Return Values :  E_OK                - success
+|                    != E_OK             - error from subordinate call
++--------------------------------------------------------------------------*/
+static int
+PN_Next_Page(FORM *form)
+{
+  T((T_CALLED("PN_Next_Page(%p)"), form));
+  returnCode(_nc_Set_Form_Page(form, Next_Page_Number(form), (FIELD *)0));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int PN_Previous_Page(FORM * form)
+|
+|   Description   :  Move to the previous page of the form
+|
+|   Return Values :  E_OK              - success
+|                    != E_OK           - error from subordinate call
++--------------------------------------------------------------------------*/
+static int
+PN_Previous_Page(FORM *form)
+{
+  T((T_CALLED("PN_Previous_Page(%p)"), form));
+  returnCode(_nc_Set_Form_Page(form, Previous_Page_Number(form), (FIELD *)0));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int PN_First_Page(FORM * form)
+|
+|   Description   :  Move to the first page of the form
+|
+|   Return Values :  E_OK              - success
+|                    != E_OK           - error from subordinate call
++--------------------------------------------------------------------------*/
+static int
+PN_First_Page(FORM *form)
+{
+  T((T_CALLED("PN_First_Page(%p)"), form));
+  returnCode(_nc_Set_Form_Page(form, 0, (FIELD *)0));
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int PN_Last_Page(FORM * form)
+|
+|   Description   :  Move to the last page of the form
+|
+|   Return Values :  E_OK              - success
+|                    != E_OK           - error from subordinate call
++--------------------------------------------------------------------------*/
+static int
+PN_Last_Page(FORM *form)
+{
+  T((T_CALLED("PN_Last_Page(%p)"), form));
+  returnCode(_nc_Set_Form_Page(form, form->maxpage - 1, (FIELD *)0));
+}
+
+/*----------------------------------------------------------------------------
+  END of Field Navigation routines
+  --------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------
+  Helper routines for the core form driver.
+  --------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  static int Data_Entry(FORM * form,int c)
+|
+|   Description   :  Enter character c into at the current position of the
+|                    current field of the form.
+|
+|   Return Values :  E_OK              - success
+|                    E_REQUEST_DENIED  - driver could not process the request
+|                    E_SYSTEM_ERROR    -
++--------------------------------------------------------------------------*/
+static int
+Data_Entry(FORM *form, int c)
+{
+  FIELD *field = form->current;
+  int result = E_REQUEST_DENIED;
+
+  T((T_CALLED("Data_Entry(%p,%s)"), form, _tracechtype((chtype)c)));
+  if ((field->opts & O_EDIT)
+#if FIX_FORM_INACTIVE_BUG
+      && (field->opts & O_ACTIVE)
+#endif
+    )
+    {
+      if ((field->opts & O_BLANK) &&
+	  First_Position_In_Current_Field(form) &&
+	  !(form->status & _FCHECK_REQUIRED) &&
+	  !(form->status & _WINDOW_MODIFIED))
+	werase(form->w);
+
+      if (form->status & _OVLMODE)
+	{
+	  waddch(form->w, (chtype)c);
+	}
+      else
+	/* no _OVLMODE */
+	{
+	  bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
+
+	  if (!(There_Is_Room ||
+		((Single_Line_Field(field) && Growable(field)))))
+	    RETURN(E_REQUEST_DENIED);
+
+	  if (!There_Is_Room && !Field_Grown(field, 1))
+	    RETURN(E_SYSTEM_ERROR);
+
+	  winsch(form->w, (chtype)c);
+	}
+
+      if ((result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form)) == E_OK)
+	{
+	  bool End_Of_Field = (((field->drows - 1) == form->currow) &&
+			       ((field->dcols - 1) == form->curcol));
+
+	  form->status |= _WINDOW_MODIFIED;
+	  if (End_Of_Field && !Growable(field) && (field->opts & O_AUTOSKIP))
+	    result = Inter_Field_Navigation(FN_Next_Field, form);
+	  else
+	    {
+	      if (End_Of_Field && Growable(field) && !Field_Grown(field, 1))
+		result = E_SYSTEM_ERROR;
+	      else
+		{
+#if USE_WIDEC_SUPPORT
+		  /*
+		   * We have just added a byte to the form field.  It may have
+		   * been part of a multibyte character.  If it was, the
+		   * addch_used field is nonzero and we should not try to move
+		   * to a new column.
+		   */
+		  if (WINDOW_EXT(form->w, addch_used) == 0)
+		    IFN_Next_Character(form);
+#else
+		  IFN_Next_Character(form);
+#endif
+		  result = E_OK;
+		}
+	    }
+	}
+    }
+  RETURN(result);
+}
+
+/* Structure to describe the binding of a request code to a function.
+   The member keycode codes the request value as well as the generic
+   routine to use for the request. The code for the generic routine
+   is coded in the upper 16 Bits while the request code is coded in
+   the lower 16 bits.
+
+   In terms of C++ you might think of a request as a class with a
+   virtual method "perform". The different types of request are
+   derived from this base class and overload (or not) the base class
+   implementation of perform.
+*/
+typedef struct
+{
+  int keycode;			/* must be at least 32 bit: hi:mode, lo: key */
+  int (*cmd) (FORM *);		/* low level driver routine for this key     */
+}
+Binding_Info;
+
+/* You may see this is the class-id of the request type class */
+#define ID_PN    (0x00000000)	/* Page navigation           */
+#define ID_FN    (0x00010000)	/* Inter-Field navigation    */
+#define ID_IFN   (0x00020000)	/* Intra-Field navigation    */
+#define ID_VSC   (0x00030000)	/* Vertical Scrolling        */
+#define ID_HSC   (0x00040000)	/* Horizontal Scrolling      */
+#define ID_FE    (0x00050000)	/* Field Editing             */
+#define ID_EM    (0x00060000)	/* Edit Mode                 */
+#define ID_FV    (0x00070000)	/* Field Validation          */
+#define ID_CH    (0x00080000)	/* Choice                    */
+#define ID_Mask  (0xffff0000)
+#define Key_Mask (0x0000ffff)
+#define ID_Shft  (16)
+
+/* This array holds all the Binding Infos */
+/* *INDENT-OFF* */
+static const Binding_Info bindings[MAX_FORM_COMMAND - MIN_FORM_COMMAND + 1] =
+{
+  { REQ_NEXT_PAGE    |ID_PN  ,PN_Next_Page},
+  { REQ_PREV_PAGE    |ID_PN  ,PN_Previous_Page},
+  { REQ_FIRST_PAGE   |ID_PN  ,PN_First_Page},
+  { REQ_LAST_PAGE    |ID_PN  ,PN_Last_Page},
+
+  { REQ_NEXT_FIELD   |ID_FN  ,FN_Next_Field},
+  { REQ_PREV_FIELD   |ID_FN  ,FN_Previous_Field},
+  { REQ_FIRST_FIELD  |ID_FN  ,FN_First_Field},
+  { REQ_LAST_FIELD   |ID_FN  ,FN_Last_Field},
+  { REQ_SNEXT_FIELD  |ID_FN  ,FN_Sorted_Next_Field},
+  { REQ_SPREV_FIELD  |ID_FN  ,FN_Sorted_Previous_Field},
+  { REQ_SFIRST_FIELD |ID_FN  ,FN_Sorted_First_Field},
+  { REQ_SLAST_FIELD  |ID_FN  ,FN_Sorted_Last_Field},
+  { REQ_LEFT_FIELD   |ID_FN  ,FN_Left_Field},
+  { REQ_RIGHT_FIELD  |ID_FN  ,FN_Right_Field},
+  { REQ_UP_FIELD     |ID_FN  ,FN_Up_Field},
+  { REQ_DOWN_FIELD   |ID_FN  ,FN_Down_Field},
+
+  { REQ_NEXT_CHAR    |ID_IFN ,IFN_Next_Character},
+  { REQ_PREV_CHAR    |ID_IFN ,IFN_Previous_Character},
+  { REQ_NEXT_LINE    |ID_IFN ,IFN_Next_Line},
+  { REQ_PREV_LINE    |ID_IFN ,IFN_Previous_Line},
+  { REQ_NEXT_WORD    |ID_IFN ,IFN_Next_Word},
+  { REQ_PREV_WORD    |ID_IFN ,IFN_Previous_Word},
+  { REQ_BEG_FIELD    |ID_IFN ,IFN_Beginning_Of_Field},
+  { REQ_END_FIELD    |ID_IFN ,IFN_End_Of_Field},
+  { REQ_BEG_LINE     |ID_IFN ,IFN_Beginning_Of_Line},
+  { REQ_END_LINE     |ID_IFN ,IFN_End_Of_Line},
+  { REQ_LEFT_CHAR    |ID_IFN ,IFN_Left_Character},
+  { REQ_RIGHT_CHAR   |ID_IFN ,IFN_Right_Character},
+  { REQ_UP_CHAR      |ID_IFN ,IFN_Up_Character},
+  { REQ_DOWN_CHAR    |ID_IFN ,IFN_Down_Character},
+
+  { REQ_NEW_LINE     |ID_FE  ,FE_New_Line},
+  { REQ_INS_CHAR     |ID_FE  ,FE_Insert_Character},
+  { REQ_INS_LINE     |ID_FE  ,FE_Insert_Line},
+  { REQ_DEL_CHAR     |ID_FE  ,FE_Delete_Character},
+  { REQ_DEL_PREV     |ID_FE  ,FE_Delete_Previous},
+  { REQ_DEL_LINE     |ID_FE  ,FE_Delete_Line},
+  { REQ_DEL_WORD     |ID_FE  ,FE_Delete_Word},
+  { REQ_CLR_EOL      |ID_FE  ,FE_Clear_To_End_Of_Line},
+  { REQ_CLR_EOF      |ID_FE  ,FE_Clear_To_End_Of_Field},
+  { REQ_CLR_FIELD    |ID_FE  ,FE_Clear_Field},
+
+  { REQ_OVL_MODE     |ID_EM  ,EM_Overlay_Mode},
+  { REQ_INS_MODE     |ID_EM  ,EM_Insert_Mode},
+
+  { REQ_SCR_FLINE    |ID_VSC ,VSC_Scroll_Line_Forward},
+  { REQ_SCR_BLINE    |ID_VSC ,VSC_Scroll_Line_Backward},
+  { REQ_SCR_FPAGE    |ID_VSC ,VSC_Scroll_Page_Forward},
+  { REQ_SCR_BPAGE    |ID_VSC ,VSC_Scroll_Page_Backward},
+  { REQ_SCR_FHPAGE   |ID_VSC ,VSC_Scroll_Half_Page_Forward},
+  { REQ_SCR_BHPAGE   |ID_VSC ,VSC_Scroll_Half_Page_Backward},
+
+  { REQ_SCR_FCHAR    |ID_HSC ,HSC_Scroll_Char_Forward},
+  { REQ_SCR_BCHAR    |ID_HSC ,HSC_Scroll_Char_Backward},
+  { REQ_SCR_HFLINE   |ID_HSC ,HSC_Horizontal_Line_Forward},
+  { REQ_SCR_HBLINE   |ID_HSC ,HSC_Horizontal_Line_Backward},
+  { REQ_SCR_HFHALF   |ID_HSC ,HSC_Horizontal_Half_Line_Forward},
+  { REQ_SCR_HBHALF   |ID_HSC ,HSC_Horizontal_Half_Line_Backward},
+
+  { REQ_VALIDATION   |ID_FV  ,FV_Validation},
+
+  { REQ_NEXT_CHOICE  |ID_CH  ,CR_Next_Choice},
+  { REQ_PREV_CHOICE  |ID_CH  ,CR_Previous_Choice}
+};
+/* *INDENT-ON* */
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  int form_driver(FORM * form,int  c)
+|
+|   Description   :  This is the workhorse of the forms system. It checks
+|                    to determine whether the character c is a request or
+|                    data. If it is a request, the form driver executes
+|                    the request and returns the result. If it is data
+|                    (printable character), it enters the data into the
+|                    current position in the current field. If it is not
+|                    recognized, the form driver assumes it is an application
+|                    defined command and returns E_UNKNOWN_COMMAND.
+|                    Application defined command should be defined relative
+|                    to MAX_FORM_COMMAND, the maximum value of a request.
+|
+|   Return Values :  E_OK              - success
+|                    E_SYSTEM_ERROR    - system error
+|                    E_BAD_ARGUMENT    - an argument is incorrect
+|                    E_NOT_POSTED      - form is not posted
+|                    E_INVALID_FIELD   - field contents are invalid
+|                    E_BAD_STATE       - called from inside a hook routine
+|                    E_REQUEST_DENIED  - request failed
+|                    E_NOT_CONNECTED   - no fields are connected to the form
+|                    E_UNKNOWN_COMMAND - command not known
++--------------------------------------------------------------------------*/
+NCURSES_EXPORT(int)
+form_driver(FORM *form, int c)
+{
+  const Binding_Info *BI = (Binding_Info *) 0;
+  int res = E_UNKNOWN_COMMAND;
+
+  T((T_CALLED("form_driver(%p,%d)"), form, c));
+
+  if (!form)
+    RETURN(E_BAD_ARGUMENT);
+
+  if (!(form->field))
+    RETURN(E_NOT_CONNECTED);
+
+  assert(form->page);
+
+  if (c == FIRST_ACTIVE_MAGIC)
+    {
+      form->current = _nc_First_Active_Field(form);
+      RETURN(E_OK);
+    }
+
+  assert(form->current &&
+	 form->current->buf &&
+	 (form->current->form == form)
+    );
+
+  if (form->status & _IN_DRIVER)
+    RETURN(E_BAD_STATE);
+
+  if (!(form->status & _POSTED))
+    RETURN(E_NOT_POSTED);
+
+  if ((c >= MIN_FORM_COMMAND && c <= MAX_FORM_COMMAND) &&
+      ((bindings[c - MIN_FORM_COMMAND].keycode & Key_Mask) == c))
+    BI = &(bindings[c - MIN_FORM_COMMAND]);
+
+  if (BI)
+    {
+      typedef int (*Generic_Method) (int (*const) (FORM *), FORM *);
+      static const Generic_Method Generic_Methods[] =
+      {
+	Page_Navigation,	/* overloaded to call field&form hooks */
+	Inter_Field_Navigation,	/* overloaded to call field hooks      */
+	NULL,			/* Intra-Field is generic              */
+	Vertical_Scrolling,	/* Overloaded to check multi-line      */
+	Horizontal_Scrolling,	/* Overloaded to check single-line     */
+	Field_Editing,		/* Overloaded to mark modification     */
+	NULL,			/* Edit Mode is generic                */
+	NULL,			/* Field Validation is generic         */
+	NULL			/* Choice Request is generic           */
+      };
+      size_t nMethods = (sizeof(Generic_Methods) / sizeof(Generic_Methods[0]));
+      size_t method = (BI->keycode >> ID_Shft) & 0xffff;	/* see ID_Mask */
+
+      if ((method >= nMethods) || !(BI->cmd))
+	res = E_SYSTEM_ERROR;
+      else
+	{
+	  Generic_Method fct = Generic_Methods[method];
+
+	  if (fct)
+	    res = fct(BI->cmd, form);
+	  else
+	    res = (BI->cmd) (form);
+	}
+    }
+#ifdef NCURSES_MOUSE_VERSION
+  else if (KEY_MOUSE == c)
+    {
+      MEVENT event;
+      WINDOW *win = form->win ? form->win : stdscr;
+      WINDOW *sub = form->sub ? form->sub : win;
+
+      getmouse(&event);
+      if ((event.bstate & (BUTTON1_CLICKED |
+			   BUTTON1_DOUBLE_CLICKED |
+			   BUTTON1_TRIPLE_CLICKED))
+	  && wenclose(win, event.y, event.x))
+	{			/* we react only if the click was in the userwin, that means
+				 * inside the form display area or at the decoration window.
+				 */
+	  int ry = event.y, rx = event.x;	/* screen coordinates */
+
+	  res = E_REQUEST_DENIED;
+	  if (mouse_trafo(&ry, &rx, FALSE))
+	    {			/* rx, ry are now "curses" coordinates */
+	      if (ry < sub->_begy)
+		{		/* we clicked above the display region; this is
+				 * interpreted as "scroll up" request
+				 */
+		  if (event.bstate & BUTTON1_CLICKED)
+		    res = form_driver(form, REQ_PREV_FIELD);
+		  else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
+		    res = form_driver(form, REQ_PREV_PAGE);
+		  else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
+		    res = form_driver(form, REQ_FIRST_FIELD);
+		}
+	      else if (ry > sub->_begy + sub->_maxy)
+		{		/* we clicked below the display region; this is
+				 * interpreted as "scroll down" request
+				 */
+		  if (event.bstate & BUTTON1_CLICKED)
+		    res = form_driver(form, REQ_NEXT_FIELD);
+		  else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
+		    res = form_driver(form, REQ_NEXT_PAGE);
+		  else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
+		    res = form_driver(form, REQ_LAST_FIELD);
+		}
+	      else if (wenclose(sub, event.y, event.x))
+		{		/* Inside the area we try to find the hit item */
+		  int i;
+
+		  ry = event.y;
+		  rx = event.x;
+		  if (wmouse_trafo(sub, &ry, &rx, FALSE))
+		    {
+		      int min_field = form->page[form->curpage].pmin;
+		      int max_field = form->page[form->curpage].pmax;
+
+		      for (i = min_field; i <= max_field; ++i)
+			{
+			  FIELD *field = form->field[i];
+
+			  if (Field_Is_Selectable(field)
+			      && Field_encloses(field, ry, rx) == E_OK)
+			    {
+			      res = _nc_Set_Current_Field(form, field);
+			      if (res == E_OK)
+				res = _nc_Position_Form_Cursor(form);
+			      if (res == E_OK
+				  && (event.bstate & BUTTON1_DOUBLE_CLICKED))
+				res = E_UNKNOWN_COMMAND;
+			      break;
+			    }
+			}
+		    }
+		}
+	    }
+	}
+      else
+	res = E_REQUEST_DENIED;
+    }
+#endif /* NCURSES_MOUSE_VERSION */
+  else if (!(c & (~(int)MAX_REGULAR_CHARACTER)))
+    {
+      /*
+       * If we're using 8-bit characters, iscntrl+isprint cover the whole set.
+       * But with multibyte characters, there is a third possibility, i.e.,
+       * parts of characters that build up into printable characters which are
+       * not considered printable.
+       *
+       * FIXME: the wide-character branch should also use Check_Char().
+       */
+#if USE_WIDEC_SUPPORT
+      if (!iscntrl(UChar(c)))
+#else
+      if (isprint(UChar(c)) &&
+	  Check_Char(form->current->type, c,
+		     (TypeArgument *)(form->current->arg)))
+#endif
+	res = Data_Entry(form, c);
+    }
+  _nc_Refresh_Current_Field(form);
+  RETURN(res);
+}
+
+/*----------------------------------------------------------------------------
+  Field-Buffer manipulation routines.
+  The effects of setting a buffer are tightly coupled to the core of the form
+  driver logic. This is especially true in the case of growable fields.
+  So I don't separate this into a separate module.
+  --------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  int set_field_buffer(FIELD *field,
+|                                         int buffer, char *value)
+|
+|   Description   :  Set the given buffer of the field to the given value.
+|                    Buffer 0 stores the displayed content of the field.
+|                    For dynamic fields this may grow the fieldbuffers if
+|                    the length of the value exceeds the current buffer
+|                    length. For buffer 0 only printable values are allowed.
+|                    For static fields, the value needs not to be zero ter-
+|                    minated. It is copied up to the length of the buffer.
+|
+|   Return Values :  E_OK            - success
+|                    E_BAD_ARGUMENT  - invalid argument
+|                    E_SYSTEM_ERROR  - system error
++--------------------------------------------------------------------------*/
+NCURSES_EXPORT(int)
+set_field_buffer(FIELD *field, int buffer, const char *value)
+{
+  FIELD_CELL *p;
+  int res = E_OK;
+  unsigned int i;
+  unsigned int len;
+
+#if USE_WIDEC_SUPPORT
+  FIELD_CELL *widevalue = 0;
+#endif
+
+  T((T_CALLED("set_field_buffer(%p,%d,%s)"), field, buffer, _nc_visbuf(value)));
+
+  if (!field || !value || ((buffer < 0) || (buffer > field->nbuf)))
+    RETURN(E_BAD_ARGUMENT);
+
+  len = Buffer_Length(field);
+
+  if (Growable(field))
+    {
+      /* for a growable field we must assume zero terminated strings, because
+         somehow we have to detect the length of what should be copied.
+       */
+      unsigned int vlen = strlen(value);
+
+      if (vlen > len)
+	{
+	  if (!Field_Grown(field,
+			   (int)(1 + (vlen - len) / ((field->rows + field->nrow)
+						     * field->cols))))
+	    RETURN(E_SYSTEM_ERROR);
+
+	  len = vlen;
+	}
+    }
+
+  p = Address_Of_Nth_Buffer(field, buffer);
+
+#if USE_WIDEC_SUPPORT
+  /*
+   * Use addstr's logic for converting a string to an array of cchar_t's.
+   * There should be a better way, but this handles nonspacing characters
+   * and other special cases that we really do not want to handle here.
+   */
+#if NCURSES_EXT_FUNCS
+  if (wresize(field->working, field->drows, field->dcols) == ERR)
+#endif
+    {
+      delwin(field->working);
+      field->working = newpad(field->drows, field->dcols);
+    }
+  wclear(field->working);
+  mvwaddstr(field->working, 0, 0, value);
+
+  if ((widevalue = typeCalloc(FIELD_CELL, len + 1)) == 0)
+    {
+      RETURN(E_SYSTEM_ERROR);
+    }
+  else
+    {
+      for (i = 0; i < (unsigned)field->drows; ++i)
+	{
+	  mvwin_wchnstr(field->working, i, 0,
+			widevalue + (i * field->dcols),
+			field->dcols);
+	}
+      for (i = 0; i < len; ++i)
+	{
+	  if (CharEq(myZEROS, widevalue[i]))
+	    {
+	      while (i < len)
+		p[i++] = myBLANK;
+	      break;
+	    }
+	  p[i] = widevalue[i];
+	}
+      free(widevalue);
+    }
+#else
+  for (i = 0; i < len; ++i)
+    {
+      if (value[i] == '\0')
+	{
+	  while (i < len)
+	    p[i++] = myBLANK;
+	  break;
+	}
+      p[i] = value[i];
+    }
+#endif
+
+  if (buffer == 0)
+    {
+      int syncres;
+
+      if (((syncres = Synchronize_Field(field)) != E_OK) &&
+	  (res == E_OK))
+	res = syncres;
+      if (((syncres = Synchronize_Linked_Fields(field)) != E_OK) &&
+	  (res == E_OK))
+	res = syncres;
+    }
+  RETURN(res);
+}
+
+/*---------------------------------------------------------------------------
+|   Facility      :  libnform
+|   Function      :  char *field_buffer(const FIELD *field,int buffer)
+|
+|   Description   :  Return the address of the buffer for the field.
+|
+|   Return Values :  Pointer to buffer or NULL if arguments were invalid.
++--------------------------------------------------------------------------*/
+NCURSES_EXPORT(char *)
+field_buffer(const FIELD *field, int buffer)
+{
+  char *result = 0;
+
+  T((T_CALLED("field_buffer(%p,%d)"), field, buffer));
+
+  if (field && (buffer >= 0) && (buffer <= field->nbuf))
+    {
+#if USE_WIDEC_SUPPORT
+      FIELD_CELL *data = Address_Of_Nth_Buffer(field, buffer);
+      unsigned need = 0;
+      int size = Buffer_Length(field);
+      int n;
+
+      /* determine the number of bytes needed to store the expanded string */
+      for (n = 0; n < size; ++n)
+	{
+	  if (!isWidecExt(data[n]))
+	    {
+	      mbstate_t state;
+	      size_t next;
+
+	      init_mb(state);
+	      next = _nc_wcrtomb(0, data[n].chars[0], &state);
+	      if (!isEILSEQ(next))
+		need += next;
+	    }
+	}
+
+      /* allocate a place to store the expanded string */
+      if (field->expanded[buffer] != 0)
+	free(field->expanded[buffer]);
+      field->expanded[buffer] = typeMalloc(char, need + 1);
+
+      /* expand the multibyte data */
+      if ((result = field->expanded[buffer]) != 0)
+	{
+	  wclear(field->working);
+	  mvwadd_wchnstr(field->working, 0, 0, data, size);
+	  mvwinnstr(field->working, 0, 0, result, (int)need);
+	}
+#else
+      result = Address_Of_Nth_Buffer(field, buffer);
+#endif
+    }
+  returnPtr(result);
+}
+
+#if USE_WIDEC_SUPPORT
+
+/* FIXME: see lib_get_wch.c */
+#if HAVE_MBTOWC && HAVE_MBLEN
+#define reset_mbytes(state) mblen(NULL, 0), mbtowc(NULL, NULL, 0)
+#define count_mbytes(buffer,length,state) mblen(buffer,length)
+#define trans_mbytes(wch,buffer,length,state) \
+	(int) mbtowc(&wch, buffer, length)
+#elif HAVE_MBRTOWC && HAVE_MBRLEN
+#define NEED_STATE
+#define reset_mbytes(state) init_mb(state)
+#define count_mbytes(buffer,length,state) mbrlen(buffer,length,&state)
+#define trans_mbytes(wch,buffer,length,state) \
+	(int) mbrtowc(&wch, buffer, length, &state)
+#else
+make an error
+#endif
+
+/*---------------------------------------------------------------------------
+| Convert a multibyte string to a wide-character string.  The result must be
+| freed by the caller.
++--------------------------------------------------------------------------*/
+NCURSES_EXPORT(wchar_t *)
+_nc_Widen_String(char *source, int *lengthp)
+{
+  wchar_t *result = 0;
+  wchar_t wch;
+  size_t given = strlen(source);
+  size_t tries;
+  int pass;
+  int status;
+
+#ifdef NEED_STATE
+  mbstate_t state;
+#endif
+
+  for (pass = 0; pass < 2; ++pass)
+    {
+      unsigned need = 0;
+      size_t passed = 0;
+
+      while (passed < given)
+	{
+	  bool found = FALSE;
+
+	  for (tries = 1, status = 0; tries <= (given - passed); ++tries)
+	    {
+	      int save = source[passed + tries];
+
+	      source[passed + tries] = 0;
+	      reset_mbytes(state);
+	      status = trans_mbytes(wch, source + passed, tries, state);
+	      source[passed + tries] = save;
+
+	      if (status > 0)
+		{
+		  found = TRUE;
+		  break;
+		}
+	    }
+	  if (found)
+	    {
+	      if (pass)
+		{
+		  result[need] = wch;
+		}
+	      passed += status;
+	      ++need;
+	    }
+	  else
+	    {
+	      if (pass)
+		{
+		  result[need] = source[passed];
+		}
+	      ++need;
+	      ++passed;
+	    }
+	}
+
+      if (!pass)
+	{
+	  if (!need)
+	    break;
+	  result = typeCalloc(wchar_t, need);
+
+	  *lengthp = need;
+	  if (result == 0)
+	    break;
+	}
+    }
+
+  return result;
+}
+#endif
+
+/* frm_driver.c ends here */