1/****************************************************************************
2 * Copyright (c) 1998-2004,2005 Free Software Foundation, Inc.              *
3 *                                                                          *
4 * Permission is hereby granted, free of charge, to any person obtaining a  *
5 * copy of this software and associated documentation files (the            *
6 * "Software"), to deal in the Software without restriction, including      *
7 * without limitation the rights to use, copy, modify, merge, publish,      *
8 * distribute, distribute with modifications, sublicense, and/or sell       *
9 * copies of the Software, and to permit persons to whom the Software is    *
10 * furnished to do so, subject to the following conditions:                 *
11 *                                                                          *
12 * The above copyright notice and this permission notice shall be included  *
13 * in all copies or substantial portions of the Software.                   *
14 *                                                                          *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22 *                                                                          *
23 * Except as contained in this notice, the name(s) of the above copyright   *
24 * holders shall not be used in advertising or otherwise to promote the     *
25 * sale, use or other dealings in this Software without prior written       *
26 * authorization.                                                           *
27 ****************************************************************************/
28
29/****************************************************************************
30 *   Author:  Juergen Pfeifer, 1995,1997                                    *
31 ****************************************************************************/
32
33#include "form.priv.h"
34
35MODULE_ID("$Id: frm_driver.c,v 1.71 2005/10/01 19:42:40 tom Exp $")
36
37/*----------------------------------------------------------------------------
38  This is the core module of the form library. It contains the majority
39  of the driver routines as well as the form_driver function.
40
41  Essentially this module is nearly the whole library. This is because
42  all the functions in this module depends on some others in the module,
43  so it makes no sense to split them into separate files because they
44  will always be linked together. The only acceptable concern is turnaround
45  time for this module, but now we have all Pentiums or RISCs, so what!
46
47  The driver routines are grouped into nine generic categories:
48
49   a)   Page Navigation            ( all functions prefixed by PN_ )
50        The current page of the form is left and some new page is
51        entered.
52   b)   Inter-Field Navigation     ( all functions prefixed by FN_ )
53        The current field of the form is left and some new field is
54        entered.
55   c)   Intra-Field Navigation     ( all functions prefixed by IFN_ )
56        The current position in the current field is changed.
57   d)   Vertical Scrolling         ( all functions prefixed by VSC_ )
58        Essentially this is a specialization of Intra-Field navigation.
59        It has to check for a multi-line field.
60   e)   Horizontal Scrolling       ( all functions prefixed by HSC_ )
61        Essentially this is a specialization of Intra-Field navigation.
62        It has to check for a single-line field.
63   f)   Field Editing              ( all functions prefixed by FE_ )
64        The content of the current field is changed
65   g)   Edit Mode requests         ( all functions prefixed by EM_ )
66        Switching between insert and overlay mode
67   h)   Field-Validation requests  ( all functions prefixed by FV_ )
68        Perform verifications of the field.
69   i)   Choice requests            ( all functions prefixed by CR_ )
70        Requests to enumerate possible field values
71  --------------------------------------------------------------------------*/
72
73/*----------------------------------------------------------------------------
74  Some remarks on the placements of assert() macros :
75  I use them only on "strategic" places, i.e. top level entries where
76  I want to make sure that things are set correctly. Throughout subordinate
77  routines I omit them mostly.
78  --------------------------------------------------------------------------*/
79
80/*
81Some options that may effect compatibility in behavior to SVr4 forms,
82but they are here to allow a more intuitive and user friendly behavior of
83our form implementation. This doesn't affect the API, so we feel it is
84uncritical.
85
86The initial implementation tries to stay very close with the behavior
87of the original SVr4 implementation, although in some areas it is quite
88clear that this isn't the most appropriate way. As far as possible this
89sources will allow you to build a forms lib that behaves quite similar
90to SVr4, but now and in the future we will give you better options.
91Perhaps at some time we will make this configurable at runtime.
92*/
93
94/* Implement a more user-friendly previous/next word behavior */
95#define FRIENDLY_PREV_NEXT_WORD (1)
96/* Fix the wrong behavior for forms with all fields inactive */
97#define FIX_FORM_INACTIVE_BUG (1)
98/* Allow dynamic field growth also when navigating past the end */
99#define GROW_IF_NAVIGATE (1)
100
101#if USE_WIDEC_SUPPORT
102#define myADDNSTR(w, s, n) wadd_wchnstr(w, s, n)
103#define myINSNSTR(w, s, n) wins_wchnstr(w, s, n)
104#define myINNSTR(w, s, n)  fix_wchnstr(w, s, n)
105#define myWCWIDTH(w, y, x) cell_width(w, y, x)
106#else
107#define myADDNSTR(w, s, n) waddnstr(w, s, n)
108#define myINSNSTR(w, s, n) winsnstr(w, s, n)
109#define myINNSTR(w, s, n)  winnstr(w, s, n)
110#define myWCWIDTH(w, y, x) 1
111#endif
112
113/*----------------------------------------------------------------------------
114  Forward references to some internally used static functions
115  --------------------------------------------------------------------------*/
116static int Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form);
117static int FN_Next_Field(FORM *form);
118static int FN_Previous_Field(FORM *form);
119static int FE_New_Line(FORM *);
120static int FE_Delete_Previous(FORM *);
121
122/*----------------------------------------------------------------------------
123  Macro Definitions.
124
125  Some Remarks on that: I use the convention to use UPPERCASE for constants
126  defined by Macros. If I provide a macro as a kind of inline routine to
127  provide some logic, I use my Upper_Lower case style.
128  --------------------------------------------------------------------------*/
129
130/* Calculate the position of a single row in a field buffer */
131#define Position_Of_Row_In_Buffer(field,row) ((row)*(field)->dcols)
132
133/* Calculate start address for the fields buffer# N */
134#define Address_Of_Nth_Buffer(field,N) \
135  ((field)->buf + (N)*(1+Buffer_Length(field)))
136
137/* Calculate the start address of the row in the fields specified buffer# N */
138#define Address_Of_Row_In_Nth_Buffer(field,N,row) \
139  (Address_Of_Nth_Buffer(field,N) + Position_Of_Row_In_Buffer(field,row))
140
141/* Calculate the start address of the row in the fields primary buffer */
142#define Address_Of_Row_In_Buffer(field,row) \
143  Address_Of_Row_In_Nth_Buffer(field,0,row)
144
145/* Calculate the start address of the row in the forms current field
146   buffer# N */
147#define Address_Of_Current_Row_In_Nth_Buffer(form,N) \
148   Address_Of_Row_In_Nth_Buffer((form)->current,N,(form)->currow)
149
150/* Calculate the start address of the row in the forms current field
151   primary buffer */
152#define Address_Of_Current_Row_In_Buffer(form) \
153   Address_Of_Current_Row_In_Nth_Buffer(form,0)
154
155/* Calculate the address of the cursor in the forms current field
156   primary buffer */
157#define Address_Of_Current_Position_In_Nth_Buffer(form,N) \
158   (Address_Of_Current_Row_In_Nth_Buffer(form,N) + (form)->curcol)
159
160/* Calculate the address of the cursor in the forms current field
161   buffer# N */
162#define Address_Of_Current_Position_In_Buffer(form) \
163  Address_Of_Current_Position_In_Nth_Buffer(form,0)
164
165/* Logic to decide whether or not a field is actually a field with
166   vertical or horizontal scrolling */
167#define Is_Scroll_Field(field)          \
168   (((field)->drows > (field)->rows) || \
169    ((field)->dcols > (field)->cols))
170
171/* Logic to decide whether or not a field needs to have an individual window
172   instead of a derived window because it contains invisible parts.
173   This is true for non-public fields and for scrollable fields. */
174#define Has_Invisible_Parts(field)     \
175  (!((field)->opts & O_PUBLIC)      || \
176   Is_Scroll_Field(field))
177
178/* Logic to decide whether or not a field needs justification */
179#define Justification_Allowed(field)        \
180   (((field)->just != NO_JUSTIFICATION)  && \
181    (Single_Line_Field(field))           && \
182    (((field)->dcols == (field)->cols)   && \
183    ((field)->opts & O_STATIC))             )
184
185/* Logic to determine whether or not a dynamic field may still grow */
186#define Growable(field) ((field)->status & _MAY_GROW)
187
188/* Macro to set the attributes for a fields window */
189#define Set_Field_Window_Attributes(field,win) \
190(  wbkgdset((win),(chtype)((field)->pad | (field)->back)), \
191   wattrset((win),(field)->fore) )
192
193/* Logic to decide whether or not a field really appears on the form */
194#define Field_Really_Appears(field)         \
195  ((field->form)                          &&\
196   (field->form->status & _POSTED)        &&\
197   (field->opts & O_VISIBLE)              &&\
198   (field->page == field->form->curpage))
199
200/* Logic to determine whether or not we are on the first position in the
201   current field */
202#define First_Position_In_Current_Field(form) \
203  (((form)->currow==0) && ((form)->curcol==0))
204
205#define Minimum(a,b) (((a)<=(b)) ? (a) : (b))
206#define Maximum(a,b) (((a)>=(b)) ? (a) : (b))
207
208/*----------------------------------------------------------------------------
209  Useful constants
210  --------------------------------------------------------------------------*/
211static FIELD_CELL myBLANK = BLANK;
212static FIELD_CELL myZEROS;
213
214#ifdef TRACE
215static void
216check_pos(FORM *form, int lineno)
217{
218  int y, x;
219
220  if (form && form->w)
221    {
222      getyx(form->w, y, x);
223      if (y != form->currow || x != form->curcol)
224	{
225	  T(("CHECKPOS %s@%d have position %d,%d vs want %d,%d",
226	     __FILE__, lineno,
227	     y, x,
228	     form->currow, form->curcol));
229	}
230    }
231}
232#define CHECKPOS(form) check_pos(form, __LINE__)
233#else
234#define CHECKPOS(form)		/* nothing */
235#endif
236
237/*----------------------------------------------------------------------------
238  Wide-character special functions
239  --------------------------------------------------------------------------*/
240#if USE_WIDEC_SUPPORT
241/* like winsnstr */
242static int
243wins_wchnstr(WINDOW *w, cchar_t *s, int n)
244{
245  int code = ERR;
246  int y, x;
247
248  while (n-- > 0)
249    {
250      getyx(w, y, x);
251      if ((code = wins_wch(w, s++)) != OK)
252	break;
253      if ((code = wmove(w, y, x + 1)) != OK)
254	break;
255    }
256  return code;
257}
258
259/* win_wchnstr is inconsistent with winnstr, since it returns OK rather than
260 * the number of items transferred.
261 */
262static int
263fix_wchnstr(WINDOW *w, cchar_t *s, int n)
264{
265  win_wchnstr(w, s, n);
266  return n;
267}
268
269/*
270 * Returns the column of the base of the given cell.
271 */
272static int
273cell_base(WINDOW *win, int y, int x)
274{
275  int result = x;
276
277  while (LEGALYX(win, y, x))
278    {
279      cchar_t *data = &(win->_line[y].text[x]);
280
281      if (isWidecBase(CHDEREF(data)) || !isWidecExt(CHDEREF(data)))
282	{
283	  result = x;
284	  break;
285	}
286      --x;
287    }
288  return result;
289}
290
291/*
292 * Returns the number of columns needed for the given cell in a window.
293 */
294static int
295cell_width(WINDOW *win, int y, int x)
296{
297  int result = 1;
298
299  if (LEGALYX(win, y, x))
300    {
301      cchar_t *data = &(win->_line[y].text[x]);
302
303      if (isWidecExt(CHDEREF(data)))
304	{
305	  /* recur, providing the number of columns to the next character */
306	  result = cell_width(win, y, x - 1);
307	}
308      else
309	{
310	  result = wcwidth(CharOf(CHDEREF(data)));
311	}
312    }
313  return result;
314}
315
316/*
317 * There is no wide-character function such as wdel_wch(), so we must find
318 * all of the cells that comprise a multi-column character and delete them
319 * one-by-one.
320 */
321static void
322delete_char(FORM *form)
323{
324  int cells = cell_width(form->w, form->currow, form->curcol);
325
326  form->curcol = cell_base(form->w, form->currow, form->curcol);
327  wmove(form->w, form->currow, form->curcol);
328  while (cells-- > 0)
329    {
330      wdelch(form->w);
331    }
332}
333#define DeleteChar(form) delete_char(form)
334#else
335#define DeleteChar(form) \
336	  wmove((form)->w, (form)->currow, (form)->curcol), \
337	  wdelch((form)->w)
338#endif
339
340/*---------------------------------------------------------------------------
341|   Facility      :  libnform
342|   Function      :  static char *Get_Start_Of_Data(char * buf, int blen)
343|
344|   Description   :  Return pointer to first non-blank position in buffer.
345|                    If buffer is empty return pointer to buffer itself.
346|
347|   Return Values :  Pointer to first non-blank position in buffer
348+--------------------------------------------------------------------------*/
349INLINE static FIELD_CELL *
350Get_Start_Of_Data(FIELD_CELL *buf, int blen)
351{
352  FIELD_CELL *p = buf;
353  FIELD_CELL *end = &buf[blen];
354
355  assert(buf && blen >= 0);
356  while ((p < end) && ISBLANK(*p))
357    p++;
358  return ((p == end) ? buf : p);
359}
360
361/*---------------------------------------------------------------------------
362|   Facility      :  libnform
363|   Function      :  static char *After_End_Of_Data(char * buf, int blen)
364|
365|   Description   :  Return pointer after last non-blank position in buffer.
366|                    If buffer is empty, return pointer to buffer itself.
367|
368|   Return Values :  Pointer to position after last non-blank position in
369|                    buffer.
370+--------------------------------------------------------------------------*/
371INLINE static FIELD_CELL *
372After_End_Of_Data(FIELD_CELL *buf, int blen)
373{
374  FIELD_CELL *p = &buf[blen];
375
376  assert(buf && blen >= 0);
377  while ((p > buf) && ISBLANK(p[-1]))
378    p--;
379  return (p);
380}
381
382/*---------------------------------------------------------------------------
383|   Facility      :  libnform
384|   Function      :  static char *Get_First_Whitespace_Character(
385|                                     char * buf, int   blen)
386|
387|   Description   :  Position to the first whitespace character.
388|
389|   Return Values :  Pointer to first whitespace character in buffer.
390+--------------------------------------------------------------------------*/
391INLINE static FIELD_CELL *
392Get_First_Whitespace_Character(FIELD_CELL *buf, int blen)
393{
394  FIELD_CELL *p = buf;
395  FIELD_CELL *end = &p[blen];
396
397  assert(buf && blen >= 0);
398  while ((p < end) && !ISBLANK(*p))
399    p++;
400  return ((p == end) ? buf : p);
401}
402
403/*---------------------------------------------------------------------------
404|   Facility      :  libnform
405|   Function      :  static char *After_Last_Whitespace_Character(
406|                                     char * buf, int blen)
407|
408|   Description   :  Get the position after the last whitespace character.
409|
410|   Return Values :  Pointer to position after last whitespace character in
411|                    buffer.
412+--------------------------------------------------------------------------*/
413INLINE static FIELD_CELL *
414After_Last_Whitespace_Character(FIELD_CELL *buf, int blen)
415{
416  FIELD_CELL *p = &buf[blen];
417
418  assert(buf && blen >= 0);
419  while ((p > buf) && !ISBLANK(p[-1]))
420    p--;
421  return (p);
422}
423
424/* Set this to 1 to use the div_t version. This is a good idea if your
425   compiler has an intrinsic div() support. Unfortunately GNU-C has it
426   not yet.
427   N.B.: This only works if form->curcol follows immediately form->currow
428         and both are of type int.
429*/
430#define USE_DIV_T (0)
431
432/*---------------------------------------------------------------------------
433|   Facility      :  libnform
434|   Function      :  static void Adjust_Cursor_Position(
435|                                       FORM * form, const char * pos)
436|
437|   Description   :  Set current row and column of the form to values
438|                    corresponding to the buffer position.
439|
440|   Return Values :  -
441+--------------------------------------------------------------------------*/
442INLINE static void
443Adjust_Cursor_Position(FORM *form, const FIELD_CELL *pos)
444{
445  FIELD *field;
446  int idx;
447
448  field = form->current;
449  assert(pos >= field->buf && field->dcols > 0);
450  idx = (int)(pos - field->buf);
451#if USE_DIV_T
452  *((div_t *) & (form->currow)) = div(idx, field->dcols);
453#else
454  form->currow = idx / field->dcols;
455  form->curcol = idx - field->cols * form->currow;
456#endif
457  if (field->drows < form->currow)
458    form->currow = 0;
459}
460
461/*---------------------------------------------------------------------------
462|   Facility      :  libnform
463|   Function      :  static void Buffer_To_Window(
464|                                      const FIELD  * field,
465|                                      WINDOW * win)
466|
467|   Description   :  Copy the buffer to the window. If it is a multi-line
468|                    field, the buffer is split to the lines of the
469|                    window without any editing.
470|
471|   Return Values :  -
472+--------------------------------------------------------------------------*/
473static void
474Buffer_To_Window(const FIELD *field, WINDOW *win)
475{
476  int width, height;
477  int y, x;
478  int len;
479  int row;
480  FIELD_CELL *pBuffer;
481
482  assert(win && field);
483
484  getyx(win, y, x);
485  width = getmaxx(win);
486  height = getmaxy(win);
487
488  for (row = 0, pBuffer = field->buf;
489       row < height;
490       row++, pBuffer += width)
491    {
492      if ((len = (int)(After_End_Of_Data(pBuffer, width) - pBuffer)) > 0)
493	{
494	  wmove(win, row, 0);
495	  myADDNSTR(win, pBuffer, len);
496	}
497    }
498  wmove(win, y, x);
499}
500
501/*---------------------------------------------------------------------------
502|   Facility      :  libnform
503|   Function      :  static void Window_To_Buffer(
504|                                          WINDOW * win,
505|                                          FIELD  * field)
506|
507|   Description   :  Copy the content of the window into the buffer.
508|                    The multiple lines of a window are simply
509|                    concatenated into the buffer. Pad characters in
510|                    the window will be replaced by blanks in the buffer.
511|
512|   Return Values :  -
513+--------------------------------------------------------------------------*/
514static void
515Window_To_Buffer(WINDOW *win, FIELD *field)
516{
517  int pad;
518  int len = 0;
519  FIELD_CELL *p;
520  int row, height;
521
522  assert(win && field && field->buf);
523
524  pad = field->pad;
525  p = field->buf;
526  height = getmaxy(win);
527
528  for (row = 0; (row < height) && (row < field->drows); row++)
529    {
530      wmove(win, row, 0);
531      len += myINNSTR(win, p + len, field->dcols);
532    }
533  p[len] = myZEROS;
534
535  /* replace visual padding character by blanks in buffer */
536  if (pad != C_BLANK)
537    {
538      int i;
539
540      for (i = 0; i < len; i++, p++)
541	{
542	  if ((unsigned long)CharOf(*p) == ChCharOf(pad)
543#if USE_WIDEC_SUPPORT
544	      && p->chars[1] == 0
545#endif
546	      && AttrOf(*p) == ChAttrOf(pad))
547	    *p = myBLANK;
548	}
549    }
550}
551
552/*---------------------------------------------------------------------------
553|   Facility      :  libnform
554|   Function      :  static void Synchronize_Buffer(FORM * form)
555|
556|   Description   :  If there was a change, copy the content of the
557|                    window into the buffer, so the buffer is synchronized
558|                    with the windows content. We have to indicate that the
559|                    buffer needs validation due to the change.
560|
561|   Return Values :  -
562+--------------------------------------------------------------------------*/
563INLINE static void
564Synchronize_Buffer(FORM *form)
565{
566  if (form->status & _WINDOW_MODIFIED)
567    {
568      form->status &= ~_WINDOW_MODIFIED;
569      form->status |= _FCHECK_REQUIRED;
570      Window_To_Buffer(form->w, form->current);
571      wmove(form->w, form->currow, form->curcol);
572    }
573}
574
575/*---------------------------------------------------------------------------
576|   Facility      :  libnform
577|   Function      :  static bool Field_Grown( FIELD *field, int amount)
578|
579|   Description   :  This function is called for growable dynamic fields
580|                    only. It has to increase the buffers and to allocate
581|                    a new window for this field.
582|                    This function has the side effect to set a new
583|                    field-buffer pointer, the dcols and drows values
584|                    as well as a new current Window for the field.
585|
586|   Return Values :  TRUE     - field successfully increased
587|                    FALSE    - there was some error
588+--------------------------------------------------------------------------*/
589static bool
590Field_Grown(FIELD *field, int amount)
591{
592  bool result = FALSE;
593
594  if (field && Growable(field))
595    {
596      bool single_line_field = Single_Line_Field(field);
597      int old_buflen = Buffer_Length(field);
598      int new_buflen;
599      int old_dcols = field->dcols;
600      int old_drows = field->drows;
601      FIELD_CELL *oldbuf = field->buf;
602      FIELD_CELL *newbuf;
603
604      int growth;
605      FORM *form = field->form;
606      bool need_visual_update = ((form != (FORM *)0) &&
607				 (form->status & _POSTED) &&
608				 (form->current == field));
609
610      if (need_visual_update)
611	Synchronize_Buffer(form);
612
613      if (single_line_field)
614	{
615	  growth = field->cols * amount;
616	  if (field->maxgrow)
617	    growth = Minimum(field->maxgrow - field->dcols, growth);
618	  field->dcols += growth;
619	  if (field->dcols == field->maxgrow)
620	    field->status &= ~_MAY_GROW;
621	}
622      else
623	{
624	  growth = (field->rows + field->nrow) * amount;
625	  if (field->maxgrow)
626	    growth = Minimum(field->maxgrow - field->drows, growth);
627	  field->drows += growth;
628	  if (field->drows == field->maxgrow)
629	    field->status &= ~_MAY_GROW;
630	}
631      /* drows, dcols changed, so we get really the new buffer length */
632      new_buflen = Buffer_Length(field);
633      newbuf = (FIELD_CELL *)malloc(Total_Buffer_Size(field));
634      if (!newbuf)
635	{
636	  /* restore to previous state */
637	  field->dcols = old_dcols;
638	  field->drows = old_drows;
639	  if ((single_line_field && (field->dcols != field->maxgrow)) ||
640	      (!single_line_field && (field->drows != field->maxgrow)))
641	    field->status |= _MAY_GROW;
642	}
643      else
644	{
645	  /* Copy all the buffers.  This is the reason why we can't just use
646	   * realloc().
647	   */
648	  int i, j;
649	  FIELD_CELL *old_bp;
650	  FIELD_CELL *new_bp;
651
652	  result = TRUE;	/* allow sharing of recovery on failure */
653
654	  field->buf = newbuf;
655	  for (i = 0; i <= field->nbuf; i++)
656	    {
657	      new_bp = Address_Of_Nth_Buffer(field, i);
658	      old_bp = oldbuf + i * (1 + old_buflen);
659	      for (j = 0; j < old_buflen; ++j)
660		new_bp[j] = old_bp[j];
661	      while (j < new_buflen)
662		new_bp[j++] = myBLANK;
663	      new_bp[new_buflen] = myZEROS;
664	    }
665
666#if USE_WIDEC_SUPPORT
667	  if (wresize(field->working, 1, Buffer_Length(field) + 1) == ERR)
668	    result = FALSE;
669#endif
670
671	  if (need_visual_update && result)
672	    {
673	      WINDOW *new_window = newpad(field->drows, field->dcols);
674
675	      if (new_window != 0)
676		{
677		  assert(form != (FORM *)0);
678		  if (form->w)
679		    delwin(form->w);
680		  form->w = new_window;
681		  Set_Field_Window_Attributes(field, form->w);
682		  werase(form->w);
683		  Buffer_To_Window(field, form->w);
684		  untouchwin(form->w);
685		  wmove(form->w, form->currow, form->curcol);
686		}
687	      else
688		result = FALSE;
689	    }
690
691	  if (result)
692	    {
693	      free(oldbuf);
694	      /* reflect changes in linked fields */
695	      if (field != field->link)
696		{
697		  FIELD *linked_field;
698
699		  for (linked_field = field->link;
700		       linked_field != field;
701		       linked_field = linked_field->link)
702		    {
703		      linked_field->buf = field->buf;
704		      linked_field->drows = field->drows;
705		      linked_field->dcols = field->dcols;
706		    }
707		}
708	    }
709	  else
710	    {
711	      /* restore old state */
712	      field->dcols = old_dcols;
713	      field->drows = old_drows;
714	      field->buf = oldbuf;
715	      if ((single_line_field &&
716		   (field->dcols != field->maxgrow)) ||
717		  (!single_line_field &&
718		   (field->drows != field->maxgrow)))
719		field->status |= _MAY_GROW;
720	      free(newbuf);
721	    }
722	}
723    }
724  return (result);
725}
726
727/*---------------------------------------------------------------------------
728|   Facility      :  libnform
729|   Function      :  int _nc_Position_Form_Cursor(FORM * form)
730|
731|   Description   :  Position the cursor in the window for the current
732|                    field to be in sync. with the currow and curcol
733|                    values.
734|
735|   Return Values :  E_OK              - success
736|                    E_BAD_ARGUMENT    - invalid form pointer
737|                    E_SYSTEM_ERROR    - form has no current field or
738|                                        field-window
739+--------------------------------------------------------------------------*/
740NCURSES_EXPORT(int)
741_nc_Position_Form_Cursor(FORM *form)
742{
743  FIELD *field;
744  WINDOW *formwin;
745
746  if (!form)
747    return (E_BAD_ARGUMENT);
748
749  if (!form->w || !form->current)
750    return (E_SYSTEM_ERROR);
751
752  field = form->current;
753  formwin = Get_Form_Window(form);
754
755  wmove(form->w, form->currow, form->curcol);
756  if (Has_Invisible_Parts(field))
757    {
758      /* in this case fieldwin isn't derived from formwin, so we have
759         to move the cursor in formwin by hand... */
760      wmove(formwin,
761	    field->frow + form->currow - form->toprow,
762	    field->fcol + form->curcol - form->begincol);
763      wcursyncup(formwin);
764    }
765  else
766    wcursyncup(form->w);
767  return (E_OK);
768}
769
770/*---------------------------------------------------------------------------
771|   Facility      :  libnform
772|   Function      :  int _nc_Refresh_Current_Field(FORM * form)
773|
774|   Description   :  Propagate the changes in the fields window to the
775|                    window of the form.
776|
777|   Return Values :  E_OK              - on success
778|                    E_BAD_ARGUMENT    - invalid form pointer
779|                    E_SYSTEM_ERROR    - general error
780+--------------------------------------------------------------------------*/
781NCURSES_EXPORT(int)
782_nc_Refresh_Current_Field(FORM *form)
783{
784  WINDOW *formwin;
785  FIELD *field;
786
787  T((T_CALLED("_nc_Refresh_Current_Field(%p)"), form));
788
789  if (!form)
790    RETURN(E_BAD_ARGUMENT);
791
792  if (!form->w || !form->current)
793    RETURN(E_SYSTEM_ERROR);
794
795  field = form->current;
796  formwin = Get_Form_Window(form);
797
798  if (field->opts & O_PUBLIC)
799    {
800      if (Is_Scroll_Field(field))
801	{
802	  /* Again, in this case the fieldwin isn't derived from formwin,
803	     so we have to perform a copy operation. */
804	  if (Single_Line_Field(field))
805	    {
806	      /* horizontal scrolling */
807	      if (form->curcol < form->begincol)
808		form->begincol = form->curcol;
809	      else
810		{
811		  if (form->curcol >= (form->begincol + field->cols))
812		    form->begincol = form->curcol - field->cols + 1;
813		}
814	      copywin(form->w,
815		      formwin,
816		      0,
817		      form->begincol,
818		      field->frow,
819		      field->fcol,
820		      field->frow,
821		      field->cols + field->fcol - 1,
822		      0);
823	    }
824	  else
825	    {
826	      /* A multi-line, i.e. vertical scrolling field */
827	      int row_after_bottom, first_modified_row, first_unmodified_row;
828
829	      if (field->drows > field->rows)
830		{
831		  row_after_bottom = form->toprow + field->rows;
832		  if (form->currow < form->toprow)
833		    {
834		      form->toprow = form->currow;
835		      field->status |= _NEWTOP;
836		    }
837		  if (form->currow >= row_after_bottom)
838		    {
839		      form->toprow = form->currow - field->rows + 1;
840		      field->status |= _NEWTOP;
841		    }
842		  if (field->status & _NEWTOP)
843		    {
844		      /* means we have to copy whole range */
845		      first_modified_row = form->toprow;
846		      first_unmodified_row = first_modified_row + field->rows;
847		      field->status &= ~_NEWTOP;
848		    }
849		  else
850		    {
851		      /* we try to optimize : finding the range of touched
852		         lines */
853		      first_modified_row = form->toprow;
854		      while (first_modified_row < row_after_bottom)
855			{
856			  if (is_linetouched(form->w, first_modified_row))
857			    break;
858			  first_modified_row++;
859			}
860		      first_unmodified_row = first_modified_row;
861		      while (first_unmodified_row < row_after_bottom)
862			{
863			  if (!is_linetouched(form->w, first_unmodified_row))
864			    break;
865			  first_unmodified_row++;
866			}
867		    }
868		}
869	      else
870		{
871		  first_modified_row = form->toprow;
872		  first_unmodified_row = first_modified_row + field->rows;
873		}
874	      if (first_unmodified_row != first_modified_row)
875		copywin(form->w,
876			formwin,
877			first_modified_row,
878			0,
879			field->frow + first_modified_row - form->toprow,
880			field->fcol,
881			field->frow + first_unmodified_row - form->toprow - 1,
882			field->cols + field->fcol - 1,
883			0);
884	    }
885	  wsyncup(formwin);
886	}
887      else
888	{
889	  /* if the field-window is simply a derived window, i.e. contains no
890	   * invisible parts, the whole thing is trivial
891	   */
892	  wsyncup(form->w);
893	}
894    }
895  untouchwin(form->w);
896  returnCode(_nc_Position_Form_Cursor(form));
897}
898
899/*---------------------------------------------------------------------------
900|   Facility      :  libnform
901|   Function      :  static void Perform_Justification(
902|                                        FIELD  * field,
903|                                        WINDOW * win)
904|
905|   Description   :  Output field with requested justification
906|
907|   Return Values :  -
908+--------------------------------------------------------------------------*/
909static void
910Perform_Justification(FIELD *field, WINDOW *win)
911{
912  FIELD_CELL *bp;
913  int len;
914  int col = 0;
915
916  bp = Get_Start_Of_Data(field->buf, Buffer_Length(field));
917  len = (int)(After_End_Of_Data(field->buf, Buffer_Length(field)) - bp);
918
919  if (len > 0)
920    {
921      assert(win && (field->drows == 1) && (field->dcols == field->cols));
922
923      switch (field->just)
924	{
925	case JUSTIFY_LEFT:
926	  break;
927	case JUSTIFY_CENTER:
928	  col = (field->cols - len) / 2;
929	  break;
930	case JUSTIFY_RIGHT:
931	  col = field->cols - len;
932	  break;
933	default:
934	  break;
935	}
936
937      wmove(win, 0, col);
938      myADDNSTR(win, bp, len);
939    }
940}
941
942/*---------------------------------------------------------------------------
943|   Facility      :  libnform
944|   Function      :  static void Undo_Justification(
945|                                     FIELD  * field,
946|                                     WINDOW * win)
947|
948|   Description   :  Display field without any justification, i.e.
949|                    left justified
950|
951|   Return Values :  -
952+--------------------------------------------------------------------------*/
953static void
954Undo_Justification(FIELD *field, WINDOW *win)
955{
956  FIELD_CELL *bp;
957  int len;
958
959  bp = Get_Start_Of_Data(field->buf, Buffer_Length(field));
960  len = (int)(After_End_Of_Data(field->buf, Buffer_Length(field)) - bp);
961
962  if (len > 0)
963    {
964      assert(win);
965      wmove(win, 0, 0);
966      myADDNSTR(win, bp, len);
967    }
968}
969
970/*---------------------------------------------------------------------------
971|   Facility      :  libnform
972|   Function      :  static bool Check_Char(
973|                                           FIELDTYPE * typ,
974|                                           int ch,
975|                                           TypeArgument *argp)
976|
977|   Description   :  Perform a single character check for character ch
978|                    according to the fieldtype instance.
979|
980|   Return Values :  TRUE             - Character is valid
981|                    FALSE            - Character is invalid
982+--------------------------------------------------------------------------*/
983static bool
984Check_Char(FIELDTYPE *typ, int ch, TypeArgument *argp)
985{
986  if (typ)
987    {
988      if (typ->status & _LINKED_TYPE)
989	{
990	  assert(argp);
991	  return (
992		   Check_Char(typ->left, ch, argp->left) ||
993		   Check_Char(typ->right, ch, argp->right));
994	}
995      else
996	{
997	  if (typ->ccheck)
998	    return typ->ccheck(ch, (void *)argp);
999	}
1000    }
1001  return (!iscntrl(UChar(ch)) ? TRUE : FALSE);
1002}
1003
1004/*---------------------------------------------------------------------------
1005|   Facility      :  libnform
1006|   Function      :  static int Display_Or_Erase_Field(
1007|                                           FIELD * field,
1008|                                           bool bEraseFlag)
1009|
1010|   Description   :  Create a subwindow for the field and display the
1011|                    buffer contents (apply justification if required)
1012|                    or simply erase the field.
1013|
1014|   Return Values :  E_OK           - on success
1015|                    E_SYSTEM_ERROR - some error (typical no memory)
1016+--------------------------------------------------------------------------*/
1017static int
1018Display_Or_Erase_Field(FIELD *field, bool bEraseFlag)
1019{
1020  WINDOW *win;
1021  WINDOW *fwin;
1022
1023  if (!field)
1024    return E_SYSTEM_ERROR;
1025
1026  fwin = Get_Form_Window(field->form);
1027  win = derwin(fwin,
1028	       field->rows, field->cols, field->frow, field->fcol);
1029
1030  if (!win)
1031    return E_SYSTEM_ERROR;
1032  else
1033    {
1034      if (field->opts & O_VISIBLE)
1035	Set_Field_Window_Attributes(field, win);
1036      else
1037	wattrset(win, getattrs(fwin));
1038      werase(win);
1039    }
1040
1041  if (!bEraseFlag)
1042    {
1043      if (field->opts & O_PUBLIC)
1044	{
1045	  if (Justification_Allowed(field))
1046	    Perform_Justification(field, win);
1047	  else
1048	    Buffer_To_Window(field, win);
1049	}
1050      field->status &= ~_NEWTOP;
1051    }
1052  wsyncup(win);
1053  delwin(win);
1054  return E_OK;
1055}
1056
1057/* Macros to preset the bEraseFlag */
1058#define Display_Field(field) Display_Or_Erase_Field(field,FALSE)
1059#define Erase_Field(field)   Display_Or_Erase_Field(field,TRUE)
1060
1061/*---------------------------------------------------------------------------
1062|   Facility      :  libnform
1063|   Function      :  static int Synchronize_Field(FIELD * field)
1064|
1065|   Description   :  Synchronize the windows content with the value in
1066|                    the buffer.
1067|
1068|   Return Values :  E_OK                - success
1069|                    E_BAD_ARGUMENT      - invalid field pointer
1070|                    E_SYSTEM_ERROR      - some severe basic error
1071+--------------------------------------------------------------------------*/
1072static int
1073Synchronize_Field(FIELD *field)
1074{
1075  FORM *form;
1076  int res = E_OK;
1077
1078  if (!field)
1079    return (E_BAD_ARGUMENT);
1080
1081  if (((form = field->form) != (FORM *)0)
1082      && Field_Really_Appears(field))
1083    {
1084      if (field == form->current)
1085	{
1086	  form->currow = form->curcol = form->toprow = form->begincol = 0;
1087	  werase(form->w);
1088
1089	  if ((field->opts & O_PUBLIC) && Justification_Allowed(field))
1090	    Undo_Justification(field, form->w);
1091	  else
1092	    Buffer_To_Window(field, form->w);
1093
1094	  field->status |= _NEWTOP;
1095	  res = _nc_Refresh_Current_Field(form);
1096	}
1097      else
1098	res = Display_Field(field);
1099    }
1100  field->status |= _CHANGED;
1101  return (res);
1102}
1103
1104/*---------------------------------------------------------------------------
1105|   Facility      :  libnform
1106|   Function      :  static int Synchronize_Linked_Fields(FIELD * field)
1107|
1108|   Description   :  Propagate the Synchronize_Field function to all linked
1109|                    fields. The first error that occurs in the sequence
1110|                    of updates is the return value.
1111|
1112|   Return Values :  E_OK                - success
1113|                    E_BAD_ARGUMENT      - invalid field pointer
1114|                    E_SYSTEM_ERROR      - some severe basic error
1115+--------------------------------------------------------------------------*/
1116static int
1117Synchronize_Linked_Fields(FIELD *field)
1118{
1119  FIELD *linked_field;
1120  int res = E_OK;
1121  int syncres;
1122
1123  if (!field)
1124    return (E_BAD_ARGUMENT);
1125
1126  if (!field->link)
1127    return (E_SYSTEM_ERROR);
1128
1129  for (linked_field = field->link;
1130       linked_field != field;
1131       linked_field = linked_field->link)
1132    {
1133      if (((syncres = Synchronize_Field(linked_field)) != E_OK) &&
1134	  (res == E_OK))
1135	res = syncres;
1136    }
1137  return (res);
1138}
1139
1140/*---------------------------------------------------------------------------
1141|   Facility      :  libnform
1142|   Function      :  int _nc_Synchronize_Attributes(FIELD * field)
1143|
1144|   Description   :  If a fields visual attributes have changed, this
1145|                    routine is called to propagate those changes to the
1146|                    screen.
1147|
1148|   Return Values :  E_OK             - success
1149|                    E_BAD_ARGUMENT   - invalid field pointer
1150|                    E_SYSTEM_ERROR   - some severe basic error
1151+--------------------------------------------------------------------------*/
1152NCURSES_EXPORT(int)
1153_nc_Synchronize_Attributes(FIELD *field)
1154{
1155  FORM *form;
1156  int res = E_OK;
1157  WINDOW *formwin;
1158
1159  T((T_CALLED("_nc_Synchronize_Attributes(%p)"), field));
1160
1161  if (!field)
1162    returnCode(E_BAD_ARGUMENT);
1163
1164  CHECKPOS(field->form);
1165  if (((form = field->form) != (FORM *)0)
1166      && Field_Really_Appears(field))
1167    {
1168      if (form->current == field)
1169	{
1170	  Synchronize_Buffer(form);
1171	  Set_Field_Window_Attributes(field, form->w);
1172	  werase(form->w);
1173	  wmove(form->w, form->currow, form->curcol);
1174
1175	  if (field->opts & O_PUBLIC)
1176	    {
1177	      if (Justification_Allowed(field))
1178		Undo_Justification(field, form->w);
1179	      else
1180		Buffer_To_Window(field, form->w);
1181	    }
1182	  else
1183	    {
1184	      formwin = Get_Form_Window(form);
1185	      copywin(form->w, formwin,
1186		      0, 0,
1187		      field->frow, field->fcol,
1188		      field->rows - 1, field->cols - 1, 0);
1189	      wsyncup(formwin);
1190	      Buffer_To_Window(field, form->w);
1191	      field->status |= _NEWTOP;		/* fake refresh to paint all */
1192	      _nc_Refresh_Current_Field(form);
1193	    }
1194	}
1195      else
1196	{
1197	  res = Display_Field(field);
1198	}
1199    }
1200  CHECKPOS(form);
1201  returnCode(res);
1202}
1203
1204/*---------------------------------------------------------------------------
1205|   Facility      :  libnform
1206|   Function      :  int _nc_Synchronize_Options(FIELD * field,
1207|                                                Field_Options newopts)
1208|
1209|   Description   :  If a fields options have changed, this routine is
1210|                    called to propagate these changes to the screen and
1211|                    to really change the behavior of the field.
1212|
1213|   Return Values :  E_OK                - success
1214|                    E_BAD_ARGUMENT      - invalid field pointer
1215|                    E_SYSTEM_ERROR      - some severe basic error
1216+--------------------------------------------------------------------------*/
1217NCURSES_EXPORT(int)
1218_nc_Synchronize_Options(FIELD *field, Field_Options newopts)
1219{
1220  Field_Options oldopts;
1221  Field_Options changed_opts;
1222  FORM *form;
1223  int res = E_OK;
1224
1225  T((T_CALLED("_nc_Synchronize_Options(%p,%#x)"), field, newopts));
1226
1227  if (!field)
1228    returnCode(E_BAD_ARGUMENT);
1229
1230  oldopts = field->opts;
1231  changed_opts = oldopts ^ newopts;
1232  field->opts = newopts;
1233  form = field->form;
1234
1235  if (form)
1236    {
1237      if (form->current == field)
1238	{
1239	  field->opts = oldopts;
1240	  returnCode(E_CURRENT);
1241	}
1242
1243      if (form->status & _POSTED)
1244	{
1245	  if ((form->curpage == field->page))
1246	    {
1247	      if (changed_opts & O_VISIBLE)
1248		{
1249		  if (newopts & O_VISIBLE)
1250		    res = Display_Field(field);
1251		  else
1252		    res = Erase_Field(field);
1253		}
1254	      else
1255		{
1256		  if ((changed_opts & O_PUBLIC) &&
1257		      (newopts & O_VISIBLE))
1258		    res = Display_Field(field);
1259		}
1260	    }
1261	}
1262    }
1263
1264  if (changed_opts & O_STATIC)
1265    {
1266      bool single_line_field = Single_Line_Field(field);
1267      int res2 = E_OK;
1268
1269      if (newopts & O_STATIC)
1270	{
1271	  /* the field becomes now static */
1272	  field->status &= ~_MAY_GROW;
1273	  /* if actually we have no hidden columns, justification may
1274	     occur again */
1275	  if (single_line_field &&
1276	      (field->cols == field->dcols) &&
1277	      (field->just != NO_JUSTIFICATION) &&
1278	      Field_Really_Appears(field))
1279	    {
1280	      res2 = Display_Field(field);
1281	    }
1282	}
1283      else
1284	{
1285	  /* field is no longer static */
1286	  if ((field->maxgrow == 0) ||
1287	      (single_line_field && (field->dcols < field->maxgrow)) ||
1288	      (!single_line_field && (field->drows < field->maxgrow)))
1289	    {
1290	      field->status |= _MAY_GROW;
1291	      /* a field with justification now changes its behavior,
1292	         so we must redisplay it */
1293	      if (single_line_field &&
1294		  (field->just != NO_JUSTIFICATION) &&
1295		  Field_Really_Appears(field))
1296		{
1297		  res2 = Display_Field(field);
1298		}
1299	    }
1300	}
1301      if (res2 != E_OK)
1302	res = res2;
1303    }
1304
1305  returnCode(res);
1306}
1307
1308/*---------------------------------------------------------------------------
1309|   Facility      :  libnform
1310|   Function      :  int _nc_Set_Current_Field(FORM  * form,
1311|                                              FIELD * newfield)
1312|
1313|   Description   :  Make the newfield the new current field.
1314|
1315|   Return Values :  E_OK                - success
1316|                    E_BAD_ARGUMENT      - invalid form or field pointer
1317|                    E_SYSTEM_ERROR      - some severe basic error
1318+--------------------------------------------------------------------------*/
1319NCURSES_EXPORT(int)
1320_nc_Set_Current_Field(FORM *form, FIELD *newfield)
1321{
1322  FIELD *field;
1323  WINDOW *new_window;
1324
1325  T((T_CALLED("_nc_Set_Current_Field(%p,%p)"), form, newfield));
1326
1327  if (!form || !newfield || !form->current || (newfield->form != form))
1328    returnCode(E_BAD_ARGUMENT);
1329
1330  if ((form->status & _IN_DRIVER))
1331    returnCode(E_BAD_STATE);
1332
1333  if (!(form->field))
1334    returnCode(E_NOT_CONNECTED);
1335
1336  field = form->current;
1337
1338  if ((field != newfield) ||
1339      !(form->status & _POSTED))
1340    {
1341      if ((form->w) &&
1342	  (field->opts & O_VISIBLE) &&
1343	  (field->form->curpage == field->page))
1344	{
1345	  _nc_Refresh_Current_Field(form);
1346	  if (field->opts & O_PUBLIC)
1347	    {
1348	      if (field->drows > field->rows)
1349		{
1350		  if (form->toprow == 0)
1351		    field->status &= ~_NEWTOP;
1352		  else
1353		    field->status |= _NEWTOP;
1354		}
1355	      else
1356		{
1357		  if (Justification_Allowed(field))
1358		    {
1359		      Window_To_Buffer(form->w, field);
1360		      werase(form->w);
1361		      Perform_Justification(field, form->w);
1362		      wsyncup(form->w);
1363		    }
1364		}
1365	    }
1366	  delwin(form->w);
1367	  form->w = (WINDOW *)0;
1368	}
1369
1370      field = newfield;
1371
1372      if (Has_Invisible_Parts(field))
1373	new_window = newpad(field->drows, field->dcols);
1374      else
1375	new_window = derwin(Get_Form_Window(form),
1376			    field->rows, field->cols, field->frow, field->fcol);
1377
1378      if (!new_window)
1379	returnCode(E_SYSTEM_ERROR);
1380
1381      form->current = field;
1382
1383      if (form->w)
1384	delwin(form->w);
1385      form->w = new_window;
1386
1387      form->status &= ~_WINDOW_MODIFIED;
1388      Set_Field_Window_Attributes(field, form->w);
1389
1390      if (Has_Invisible_Parts(field))
1391	{
1392	  werase(form->w);
1393	  Buffer_To_Window(field, form->w);
1394	}
1395      else
1396	{
1397	  if (Justification_Allowed(field))
1398	    {
1399	      werase(form->w);
1400	      Undo_Justification(field, form->w);
1401	      wsyncup(form->w);
1402	    }
1403	}
1404
1405      untouchwin(form->w);
1406    }
1407
1408  form->currow = form->curcol = form->toprow = form->begincol = 0;
1409  returnCode(E_OK);
1410}
1411
1412/*----------------------------------------------------------------------------
1413  Intra-Field Navigation routines
1414  --------------------------------------------------------------------------*/
1415
1416/*---------------------------------------------------------------------------
1417|   Facility      :  libnform
1418|   Function      :  static int IFN_Next_Character(FORM * form)
1419|
1420|   Description   :  Move to the next character in the field. In a multi-line
1421|                    field this wraps at the end of the line.
1422|
1423|   Return Values :  E_OK                - success
1424|                    E_REQUEST_DENIED    - at the rightmost position
1425+--------------------------------------------------------------------------*/
1426static int
1427IFN_Next_Character(FORM *form)
1428{
1429  FIELD *field = form->current;
1430  int step = myWCWIDTH(form->w, form->currow, form->curcol);
1431
1432  T((T_CALLED("IFN_Next_Character(%p)"), form));
1433  if ((form->curcol += step) == field->dcols)
1434    {
1435      if ((++(form->currow)) == field->drows)
1436	{
1437#if GROW_IF_NAVIGATE
1438	  if (!Single_Line_Field(field) && Field_Grown(field, 1))
1439	    {
1440	      form->curcol = 0;
1441	      returnCode(E_OK);
1442	    }
1443#endif
1444	  form->currow--;
1445#if GROW_IF_NAVIGATE
1446	  if (Single_Line_Field(field) && Field_Grown(field, 1))
1447	    returnCode(E_OK);
1448#endif
1449	  form->curcol -= step;
1450	  returnCode(E_REQUEST_DENIED);
1451	}
1452      form->curcol = 0;
1453    }
1454  returnCode(E_OK);
1455}
1456
1457/*---------------------------------------------------------------------------
1458|   Facility      :  libnform
1459|   Function      :  static int IFN_Previous_Character(FORM * form)
1460|
1461|   Description   :  Move to the previous character in the field. In a
1462|                    multi-line field this wraps and the beginning of the
1463|                    line.
1464|
1465|   Return Values :  E_OK                - success
1466|                    E_REQUEST_DENIED    - at the leftmost position
1467+--------------------------------------------------------------------------*/
1468static int
1469IFN_Previous_Character(FORM *form)
1470{
1471  int amount = myWCWIDTH(form->w, form->currow, form->curcol - 1);
1472  int oldcol = form->curcol;
1473
1474  T((T_CALLED("IFN_Previous_Character(%p)"), form));
1475  if ((form->curcol -= amount) < 0)
1476    {
1477      if ((--(form->currow)) < 0)
1478	{
1479	  form->currow++;
1480	  form->curcol = oldcol;
1481	  returnCode(E_REQUEST_DENIED);
1482	}
1483      form->curcol = form->current->dcols - 1;
1484    }
1485  returnCode(E_OK);
1486}
1487
1488/*---------------------------------------------------------------------------
1489|   Facility      :  libnform
1490|   Function      :  static int IFN_Next_Line(FORM * form)
1491|
1492|   Description   :  Move to the beginning of the next line in the field
1493|
1494|   Return Values :  E_OK                - success
1495|                    E_REQUEST_DENIED    - at the last line
1496+--------------------------------------------------------------------------*/
1497static int
1498IFN_Next_Line(FORM *form)
1499{
1500  FIELD *field = form->current;
1501
1502  T((T_CALLED("IFN_Next_Line(%p)"), form));
1503  if ((++(form->currow)) == field->drows)
1504    {
1505#if GROW_IF_NAVIGATE
1506      if (!Single_Line_Field(field) && Field_Grown(field, 1))
1507	returnCode(E_OK);
1508#endif
1509      form->currow--;
1510      returnCode(E_REQUEST_DENIED);
1511    }
1512  form->curcol = 0;
1513  returnCode(E_OK);
1514}
1515
1516/*---------------------------------------------------------------------------
1517|   Facility      :  libnform
1518|   Function      :  static int IFN_Previous_Line(FORM * form)
1519|
1520|   Description   :  Move to the beginning of the previous line in the field
1521|
1522|   Return Values :  E_OK                - success
1523|                    E_REQUEST_DENIED    - at the first line
1524+--------------------------------------------------------------------------*/
1525static int
1526IFN_Previous_Line(FORM *form)
1527{
1528  T((T_CALLED("IFN_Previous_Line(%p)"), form));
1529  if ((--(form->currow)) < 0)
1530    {
1531      form->currow++;
1532      returnCode(E_REQUEST_DENIED);
1533    }
1534  form->curcol = 0;
1535  returnCode(E_OK);
1536}
1537
1538/*---------------------------------------------------------------------------
1539|   Facility      :  libnform
1540|   Function      :  static int IFN_Next_Word(FORM * form)
1541|
1542|   Description   :  Move to the beginning of the next word in the field.
1543|
1544|   Return Values :  E_OK             - success
1545|                    E_REQUEST_DENIED - there is no next word
1546+--------------------------------------------------------------------------*/
1547static int
1548IFN_Next_Word(FORM *form)
1549{
1550  FIELD *field = form->current;
1551  FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
1552  FIELD_CELL *s;
1553  FIELD_CELL *t;
1554
1555  T((T_CALLED("IFN_Next_Word(%p)"), form));
1556
1557  /* We really need access to the data, so we have to synchronize */
1558  Synchronize_Buffer(form);
1559
1560  /* Go to the first whitespace after the current position (including
1561     current position). This is then the starting point to look for the
1562     next non-blank data */
1563  s = Get_First_Whitespace_Character(bp, Buffer_Length(field) -
1564				     (int)(bp - field->buf));
1565
1566  /* Find the start of the next word */
1567  t = Get_Start_Of_Data(s, Buffer_Length(field) -
1568			(int)(s - field->buf));
1569#if !FRIENDLY_PREV_NEXT_WORD
1570  if (s == t)
1571    returnCode(E_REQUEST_DENIED);
1572  else
1573#endif
1574    {
1575      Adjust_Cursor_Position(form, t);
1576      returnCode(E_OK);
1577    }
1578}
1579
1580/*---------------------------------------------------------------------------
1581|   Facility      :  libnform
1582|   Function      :  static int IFN_Previous_Word(FORM * form)
1583|
1584|   Description   :  Move to the beginning of the previous word in the field.
1585|
1586|   Return Values :  E_OK             - success
1587|                    E_REQUEST_DENIED - there is no previous word
1588+--------------------------------------------------------------------------*/
1589static int
1590IFN_Previous_Word(FORM *form)
1591{
1592  FIELD *field = form->current;
1593  FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
1594  FIELD_CELL *s;
1595  FIELD_CELL *t;
1596  bool again = FALSE;
1597
1598  T((T_CALLED("IFN_Previous_Word(%p)"), form));
1599
1600  /* We really need access to the data, so we have to synchronize */
1601  Synchronize_Buffer(form);
1602
1603  s = After_End_Of_Data(field->buf, (int)(bp - field->buf));
1604  /* s points now right after the last non-blank in the buffer before bp.
1605     If bp was in a word, s equals bp. In this case we must find the last
1606     whitespace in the buffer before bp and repeat the game to really find
1607     the previous word! */
1608  if (s == bp)
1609    again = TRUE;
1610
1611  /* And next call now goes backward to look for the last whitespace
1612     before that, pointing right after this, so it points to the begin
1613     of the previous word.
1614   */
1615  t = After_Last_Whitespace_Character(field->buf, (int)(s - field->buf));
1616#if !FRIENDLY_PREV_NEXT_WORD
1617  if (s == t)
1618    returnCode(E_REQUEST_DENIED);
1619#endif
1620  if (again)
1621    {
1622      /* and do it again, replacing bp by t */
1623      s = After_End_Of_Data(field->buf, (int)(t - field->buf));
1624      t = After_Last_Whitespace_Character(field->buf, (int)(s - field->buf));
1625#if !FRIENDLY_PREV_NEXT_WORD
1626      if (s == t)
1627	returnCode(E_REQUEST_DENIED);
1628#endif
1629    }
1630  Adjust_Cursor_Position(form, t);
1631  returnCode(E_OK);
1632}
1633
1634/*---------------------------------------------------------------------------
1635|   Facility      :  libnform
1636|   Function      :  static int IFN_Beginning_Of_Field(FORM * form)
1637|
1638|   Description   :  Place the cursor at the first non-pad character in
1639|                    the field.
1640|
1641|   Return Values :  E_OK             - success
1642+--------------------------------------------------------------------------*/
1643static int
1644IFN_Beginning_Of_Field(FORM *form)
1645{
1646  FIELD *field = form->current;
1647
1648  T((T_CALLED("IFN_Beginning_Of_Field(%p)"), form));
1649  Synchronize_Buffer(form);
1650  Adjust_Cursor_Position(form,
1651			 Get_Start_Of_Data(field->buf, Buffer_Length(field)));
1652  returnCode(E_OK);
1653}
1654
1655/*---------------------------------------------------------------------------
1656|   Facility      :  libnform
1657|   Function      :  static int IFN_End_Of_Field(FORM * form)
1658|
1659|   Description   :  Place the cursor after the last non-pad character in
1660|                    the field. If the field occupies the last position in
1661|                    the buffer, the cursor is positioned on the last
1662|                    character.
1663|
1664|   Return Values :  E_OK              - success
1665+--------------------------------------------------------------------------*/
1666static int
1667IFN_End_Of_Field(FORM *form)
1668{
1669  FIELD *field = form->current;
1670  FIELD_CELL *pos;
1671
1672  T((T_CALLED("IFN_End_Of_Field(%p)"), form));
1673  Synchronize_Buffer(form);
1674  pos = After_End_Of_Data(field->buf, Buffer_Length(field));
1675  if (pos == (field->buf + Buffer_Length(field)))
1676    pos--;
1677  Adjust_Cursor_Position(form, pos);
1678  returnCode(E_OK);
1679}
1680
1681/*---------------------------------------------------------------------------
1682|   Facility      :  libnform
1683|   Function      :  static int IFN_Beginning_Of_Line(FORM * form)
1684|
1685|   Description   :  Place the cursor on the first non-pad character in
1686|                    the current line of the field.
1687|
1688|   Return Values :  E_OK         - success
1689+--------------------------------------------------------------------------*/
1690static int
1691IFN_Beginning_Of_Line(FORM *form)
1692{
1693  FIELD *field = form->current;
1694
1695  T((T_CALLED("IFN_Beginning_Of_Line(%p)"), form));
1696  Synchronize_Buffer(form);
1697  Adjust_Cursor_Position(form,
1698			 Get_Start_Of_Data(Address_Of_Current_Row_In_Buffer(form),
1699					   field->dcols));
1700  returnCode(E_OK);
1701}
1702
1703/*---------------------------------------------------------------------------
1704|   Facility      :  libnform
1705|   Function      :  static int IFN_End_Of_Line(FORM * form)
1706|
1707|   Description   :  Place the cursor after the last non-pad character in the
1708|                    current line of the field. If the field occupies the
1709|                    last column in the line, the cursor is positioned on the
1710|                    last character of the line.
1711|
1712|   Return Values :  E_OK        - success
1713+--------------------------------------------------------------------------*/
1714static int
1715IFN_End_Of_Line(FORM *form)
1716{
1717  FIELD *field = form->current;
1718  FIELD_CELL *pos;
1719  FIELD_CELL *bp;
1720
1721  T((T_CALLED("IFN_End_Of_Line(%p)"), form));
1722  Synchronize_Buffer(form);
1723  bp = Address_Of_Current_Row_In_Buffer(form);
1724  pos = After_End_Of_Data(bp, field->dcols);
1725  if (pos == (bp + field->dcols))
1726    pos--;
1727  Adjust_Cursor_Position(form, pos);
1728  returnCode(E_OK);
1729}
1730
1731/*---------------------------------------------------------------------------
1732|   Facility      :  libnform
1733|   Function      :  static int IFN_Left_Character(FORM * form)
1734|
1735|   Description   :  Move one character to the left in the current line.
1736|                    This doesn't cycle.
1737|
1738|   Return Values :  E_OK             - success
1739|                    E_REQUEST_DENIED - already in first column
1740+--------------------------------------------------------------------------*/
1741static int
1742IFN_Left_Character(FORM *form)
1743{
1744  int amount = myWCWIDTH(form->w, form->currow, form->curcol - 1);
1745  int oldcol = form->curcol;
1746
1747  T((T_CALLED("IFN_Left_Character(%p)"), form));
1748  if ((form->curcol -= amount) < 0)
1749    {
1750      form->curcol = oldcol;
1751      returnCode(E_REQUEST_DENIED);
1752    }
1753  returnCode(E_OK);
1754}
1755
1756/*---------------------------------------------------------------------------
1757|   Facility      :  libnform
1758|   Function      :  static int IFN_Right_Character(FORM * form)
1759|
1760|   Description   :  Move one character to the right in the current line.
1761|                    This doesn't cycle.
1762|
1763|   Return Values :  E_OK              - success
1764|                    E_REQUEST_DENIED  - already in last column
1765+--------------------------------------------------------------------------*/
1766static int
1767IFN_Right_Character(FORM *form)
1768{
1769  int amount = myWCWIDTH(form->w, form->currow, form->curcol);
1770  int oldcol = form->curcol;
1771
1772  T((T_CALLED("IFN_Right_Character(%p)"), form));
1773  if ((form->curcol += amount) >= form->current->dcols)
1774    {
1775#if GROW_IF_NAVIGATE
1776      FIELD *field = form->current;
1777
1778      if (Single_Line_Field(field) && Field_Grown(field, 1))
1779	returnCode(E_OK);
1780#endif
1781      form->curcol = oldcol;
1782      returnCode(E_REQUEST_DENIED);
1783    }
1784  returnCode(E_OK);
1785}
1786
1787/*---------------------------------------------------------------------------
1788|   Facility      :  libnform
1789|   Function      :  static int IFN_Up_Character(FORM * form)
1790|
1791|   Description   :  Move one line up. This doesn't cycle through the lines
1792|                    of the field.
1793|
1794|   Return Values :  E_OK              - success
1795|                    E_REQUEST_DENIED  - already in last column
1796+--------------------------------------------------------------------------*/
1797static int
1798IFN_Up_Character(FORM *form)
1799{
1800  T((T_CALLED("IFN_Up_Character(%p)"), form));
1801  if ((--(form->currow)) < 0)
1802    {
1803      form->currow++;
1804      returnCode(E_REQUEST_DENIED);
1805    }
1806  returnCode(E_OK);
1807}
1808
1809/*---------------------------------------------------------------------------
1810|   Facility      :  libnform
1811|   Function      :  static int IFN_Down_Character(FORM * form)
1812|
1813|   Description   :  Move one line down. This doesn't cycle through the
1814|                    lines of the field.
1815|
1816|   Return Values :  E_OK              - success
1817|                    E_REQUEST_DENIED  - already in last column
1818+--------------------------------------------------------------------------*/
1819static int
1820IFN_Down_Character(FORM *form)
1821{
1822  FIELD *field = form->current;
1823
1824  T((T_CALLED("IFN_Down_Character(%p)"), form));
1825  if ((++(form->currow)) == field->drows)
1826    {
1827#if GROW_IF_NAVIGATE
1828      if (!Single_Line_Field(field) && Field_Grown(field, 1))
1829	returnCode(E_OK);
1830#endif
1831      --(form->currow);
1832      returnCode(E_REQUEST_DENIED);
1833    }
1834  returnCode(E_OK);
1835}
1836/*----------------------------------------------------------------------------
1837  END of Intra-Field Navigation routines
1838  --------------------------------------------------------------------------*/
1839
1840/*----------------------------------------------------------------------------
1841  Vertical scrolling helper routines
1842  --------------------------------------------------------------------------*/
1843
1844/*---------------------------------------------------------------------------
1845|   Facility      :  libnform
1846|   Function      :  static int VSC_Generic(FORM *form, int nlines)
1847|
1848|   Description   :  Scroll multi-line field forward (nlines>0) or
1849|                    backward (nlines<0) this many lines.
1850|
1851|   Return Values :  E_OK              - success
1852|                    E_REQUEST_DENIED  - can't scroll
1853+--------------------------------------------------------------------------*/
1854static int
1855VSC_Generic(FORM *form, int nlines)
1856{
1857  FIELD *field = form->current;
1858  int res = E_REQUEST_DENIED;
1859  int rows_to_go = (nlines > 0 ? nlines : -nlines);
1860
1861  if (nlines > 0)
1862    {
1863      if ((rows_to_go + form->toprow) > (field->drows - field->rows))
1864	rows_to_go = (field->drows - field->rows - form->toprow);
1865
1866      if (rows_to_go > 0)
1867	{
1868	  form->currow += rows_to_go;
1869	  form->toprow += rows_to_go;
1870	  res = E_OK;
1871	}
1872    }
1873  else
1874    {
1875      if (rows_to_go > form->toprow)
1876	rows_to_go = form->toprow;
1877
1878      if (rows_to_go > 0)
1879	{
1880	  form->currow -= rows_to_go;
1881	  form->toprow -= rows_to_go;
1882	  res = E_OK;
1883	}
1884    }
1885  return (res);
1886}
1887/*----------------------------------------------------------------------------
1888  End of Vertical scrolling helper routines
1889  --------------------------------------------------------------------------*/
1890
1891/*----------------------------------------------------------------------------
1892  Vertical scrolling routines
1893  --------------------------------------------------------------------------*/
1894
1895/*---------------------------------------------------------------------------
1896|   Facility      :  libnform
1897|   Function      :  static int Vertical_Scrolling(
1898|                                           int (* const fct) (FORM *),
1899|                                           FORM * form)
1900|
1901|   Description   :  Performs the generic vertical scrolling routines.
1902|                    This has to check for a multi-line field and to set
1903|                    the _NEWTOP flag if scrolling really occurred.
1904|
1905|   Return Values :  Propagated error code from low-level driver calls
1906+--------------------------------------------------------------------------*/
1907static int
1908Vertical_Scrolling(int (*const fct) (FORM *), FORM *form)
1909{
1910  int res = E_REQUEST_DENIED;
1911
1912  if (!Single_Line_Field(form->current))
1913    {
1914      res = fct(form);
1915      if (res == E_OK)
1916	form->current->status |= _NEWTOP;
1917    }
1918  return (res);
1919}
1920
1921/*---------------------------------------------------------------------------
1922|   Facility      :  libnform
1923|   Function      :  static int VSC_Scroll_Line_Forward(FORM * form)
1924|
1925|   Description   :  Scroll multi-line field forward a line
1926|
1927|   Return Values :  E_OK                - success
1928|                    E_REQUEST_DENIED    - no data ahead
1929+--------------------------------------------------------------------------*/
1930static int
1931VSC_Scroll_Line_Forward(FORM *form)
1932{
1933  T((T_CALLED("VSC_Scroll_Line_Forward(%p)"), form));
1934  returnCode(VSC_Generic(form, 1));
1935}
1936
1937/*---------------------------------------------------------------------------
1938|   Facility      :  libnform
1939|   Function      :  static int VSC_Scroll_Line_Backward(FORM * form)
1940|
1941|   Description   :  Scroll multi-line field backward a line
1942|
1943|   Return Values :  E_OK                - success
1944|                    E_REQUEST_DENIED    - no data behind
1945+--------------------------------------------------------------------------*/
1946static int
1947VSC_Scroll_Line_Backward(FORM *form)
1948{
1949  T((T_CALLED("VSC_Scroll_Line_Backward(%p)"), form));
1950  returnCode(VSC_Generic(form, -1));
1951}
1952
1953/*---------------------------------------------------------------------------
1954|   Facility      :  libnform
1955|   Function      :  static int VSC_Scroll_Page_Forward(FORM * form)
1956|
1957|   Description   :  Scroll a multi-line field forward a page
1958|
1959|   Return Values :  E_OK              - success
1960|                    E_REQUEST_DENIED  - no data ahead
1961+--------------------------------------------------------------------------*/
1962static int
1963VSC_Scroll_Page_Forward(FORM *form)
1964{
1965  T((T_CALLED("VSC_Scroll_Page_Forward(%p)"), form));
1966  returnCode(VSC_Generic(form, form->current->rows));
1967}
1968
1969/*---------------------------------------------------------------------------
1970|   Facility      :  libnform
1971|   Function      :  static int VSC_Scroll_Half_Page_Forward(FORM * form)
1972|
1973|   Description   :  Scroll a multi-line field forward half a page
1974|
1975|   Return Values :  E_OK              - success
1976|                    E_REQUEST_DENIED  - no data ahead
1977+--------------------------------------------------------------------------*/
1978static int
1979VSC_Scroll_Half_Page_Forward(FORM *form)
1980{
1981  T((T_CALLED("VSC_Scroll_Half_Page_Forward(%p)"), form));
1982  returnCode(VSC_Generic(form, (form->current->rows + 1) / 2));
1983}
1984
1985/*---------------------------------------------------------------------------
1986|   Facility      :  libnform
1987|   Function      :  static int VSC_Scroll_Page_Backward(FORM * form)
1988|
1989|   Description   :  Scroll a multi-line field backward a page
1990|
1991|   Return Values :  E_OK              - success
1992|                    E_REQUEST_DENIED  - no data behind
1993+--------------------------------------------------------------------------*/
1994static int
1995VSC_Scroll_Page_Backward(FORM *form)
1996{
1997  T((T_CALLED("VSC_Scroll_Page_Backward(%p)"), form));
1998  returnCode(VSC_Generic(form, -(form->current->rows)));
1999}
2000
2001/*---------------------------------------------------------------------------
2002|   Facility      :  libnform
2003|   Function      :  static int VSC_Scroll_Half_Page_Backward(FORM * form)
2004|
2005|   Description   :  Scroll a multi-line field backward half a page
2006|
2007|   Return Values :  E_OK              - success
2008|                    E_REQUEST_DENIED  - no data behind
2009+--------------------------------------------------------------------------*/
2010static int
2011VSC_Scroll_Half_Page_Backward(FORM *form)
2012{
2013  T((T_CALLED("VSC_Scroll_Half_Page_Backward(%p)"), form));
2014  returnCode(VSC_Generic(form, -((form->current->rows + 1) / 2)));
2015}
2016/*----------------------------------------------------------------------------
2017  End of Vertical scrolling routines
2018  --------------------------------------------------------------------------*/
2019
2020/*----------------------------------------------------------------------------
2021  Horizontal scrolling helper routines
2022  --------------------------------------------------------------------------*/
2023
2024/*---------------------------------------------------------------------------
2025|   Facility      :  libnform
2026|   Function      :  static int HSC_Generic(FORM *form, int ncolumns)
2027|
2028|   Description   :  Scroll single-line field forward (ncolumns>0) or
2029|                    backward (ncolumns<0) this many columns.
2030|
2031|   Return Values :  E_OK              - success
2032|                    E_REQUEST_DENIED  - can't scroll
2033+--------------------------------------------------------------------------*/
2034static int
2035HSC_Generic(FORM *form, int ncolumns)
2036{
2037  FIELD *field = form->current;
2038  int res = E_REQUEST_DENIED;
2039  int cols_to_go = (ncolumns > 0 ? ncolumns : -ncolumns);
2040
2041  if (ncolumns > 0)
2042    {
2043      if ((cols_to_go + form->begincol) > (field->dcols - field->cols))
2044	cols_to_go = field->dcols - field->cols - form->begincol;
2045
2046      if (cols_to_go > 0)
2047	{
2048	  form->curcol += cols_to_go;
2049	  form->begincol += cols_to_go;
2050	  res = E_OK;
2051	}
2052    }
2053  else
2054    {
2055      if (cols_to_go > form->begincol)
2056	cols_to_go = form->begincol;
2057
2058      if (cols_to_go > 0)
2059	{
2060	  form->curcol -= cols_to_go;
2061	  form->begincol -= cols_to_go;
2062	  res = E_OK;
2063	}
2064    }
2065  return (res);
2066}
2067/*----------------------------------------------------------------------------
2068  End of Horizontal scrolling helper routines
2069  --------------------------------------------------------------------------*/
2070
2071/*----------------------------------------------------------------------------
2072  Horizontal scrolling routines
2073  --------------------------------------------------------------------------*/
2074
2075/*---------------------------------------------------------------------------
2076|   Facility      :  libnform
2077|   Function      :  static int Horizontal_Scrolling(
2078|                                          int (* const fct) (FORM *),
2079|                                          FORM * form)
2080|
2081|   Description   :  Performs the generic horizontal scrolling routines.
2082|                    This has to check for a single-line field.
2083|
2084|   Return Values :  Propagated error code from low-level driver calls
2085+--------------------------------------------------------------------------*/
2086static int
2087Horizontal_Scrolling(int (*const fct) (FORM *), FORM *form)
2088{
2089  if (Single_Line_Field(form->current))
2090    return fct(form);
2091  else
2092    return (E_REQUEST_DENIED);
2093}
2094
2095/*---------------------------------------------------------------------------
2096|   Facility      :  libnform
2097|   Function      :  static int HSC_Scroll_Char_Forward(FORM * form)
2098|
2099|   Description   :  Scroll single-line field forward a character
2100|
2101|   Return Values :  E_OK                - success
2102|                    E_REQUEST_DENIED    - no data ahead
2103+--------------------------------------------------------------------------*/
2104static int
2105HSC_Scroll_Char_Forward(FORM *form)
2106{
2107  T((T_CALLED("HSC_Scroll_Char_Forward(%p)"), form));
2108  returnCode(HSC_Generic(form, 1));
2109}
2110
2111/*---------------------------------------------------------------------------
2112|   Facility      :  libnform
2113|   Function      :  static int HSC_Scroll_Char_Backward(FORM * form)
2114|
2115|   Description   :  Scroll single-line field backward a character
2116|
2117|   Return Values :  E_OK                - success
2118|                    E_REQUEST_DENIED    - no data behind
2119+--------------------------------------------------------------------------*/
2120static int
2121HSC_Scroll_Char_Backward(FORM *form)
2122{
2123  T((T_CALLED("HSC_Scroll_Char_Backward(%p)"), form));
2124  returnCode(HSC_Generic(form, -1));
2125}
2126
2127/*---------------------------------------------------------------------------
2128|   Facility      :  libnform
2129|   Function      :  static int HSC_Horizontal_Line_Forward(FORM* form)
2130|
2131|   Description   :  Scroll single-line field forward a line
2132|
2133|   Return Values :  E_OK                - success
2134|                    E_REQUEST_DENIED    - no data ahead
2135+--------------------------------------------------------------------------*/
2136static int
2137HSC_Horizontal_Line_Forward(FORM *form)
2138{
2139  T((T_CALLED("HSC_Horizontal_Line_Forward(%p)"), form));
2140  returnCode(HSC_Generic(form, form->current->cols));
2141}
2142
2143/*---------------------------------------------------------------------------
2144|   Facility      :  libnform
2145|   Function      :  static int HSC_Horizontal_Half_Line_Forward(FORM* form)
2146|
2147|   Description   :  Scroll single-line field forward half a line
2148|
2149|   Return Values :  E_OK               - success
2150|                    E_REQUEST_DENIED   - no data ahead
2151+--------------------------------------------------------------------------*/
2152static int
2153HSC_Horizontal_Half_Line_Forward(FORM *form)
2154{
2155  T((T_CALLED("HSC_Horizontal_Half_Line_Forward(%p)"), form));
2156  returnCode(HSC_Generic(form, (form->current->cols + 1) / 2));
2157}
2158
2159/*---------------------------------------------------------------------------
2160|   Facility      :  libnform
2161|   Function      :  static int HSC_Horizontal_Line_Backward(FORM* form)
2162|
2163|   Description   :  Scroll single-line field backward a line
2164|
2165|   Return Values :  E_OK                - success
2166|                    E_REQUEST_DENIED    - no data behind
2167+--------------------------------------------------------------------------*/
2168static int
2169HSC_Horizontal_Line_Backward(FORM *form)
2170{
2171  T((T_CALLED("HSC_Horizontal_Line_Backward(%p)"), form));
2172  returnCode(HSC_Generic(form, -(form->current->cols)));
2173}
2174
2175/*---------------------------------------------------------------------------
2176|   Facility      :  libnform
2177|   Function      :  static int HSC_Horizontal_Half_Line_Backward(FORM* form)
2178|
2179|   Description   :  Scroll single-line field backward half a line
2180|
2181|   Return Values :  E_OK                - success
2182|                    E_REQUEST_DENIED    - no data behind
2183+--------------------------------------------------------------------------*/
2184static int
2185HSC_Horizontal_Half_Line_Backward(FORM *form)
2186{
2187  T((T_CALLED("HSC_Horizontal_Half_Line_Backward(%p)"), form));
2188  returnCode(HSC_Generic(form, -((form->current->cols + 1) / 2)));
2189}
2190
2191/*----------------------------------------------------------------------------
2192  End of Horizontal scrolling routines
2193  --------------------------------------------------------------------------*/
2194
2195/*----------------------------------------------------------------------------
2196  Helper routines for Field Editing
2197  --------------------------------------------------------------------------*/
2198
2199/*---------------------------------------------------------------------------
2200|   Facility      :  libnform
2201|   Function      :  static bool Is_There_Room_For_A_Line(FORM * form)
2202|
2203|   Description   :  Check whether or not there is enough room in the
2204|                    buffer to enter a whole line.
2205|
2206|   Return Values :  TRUE   - there is enough space
2207|                    FALSE  - there is not enough space
2208+--------------------------------------------------------------------------*/
2209INLINE static bool
2210Is_There_Room_For_A_Line(FORM *form)
2211{
2212  FIELD *field = form->current;
2213  FIELD_CELL *begin_of_last_line, *s;
2214
2215  Synchronize_Buffer(form);
2216  begin_of_last_line = Address_Of_Row_In_Buffer(field, (field->drows - 1));
2217  s = After_End_Of_Data(begin_of_last_line, field->dcols);
2218  return ((s == begin_of_last_line) ? TRUE : FALSE);
2219}
2220
2221/*---------------------------------------------------------------------------
2222|   Facility      :  libnform
2223|   Function      :  static bool Is_There_Room_For_A_Char_In_Line(FORM * form)
2224|
2225|   Description   :  Checks whether or not there is room for a new character
2226|                    in the current line.
2227|
2228|   Return Values :  TRUE    - there is room
2229|                    FALSE   - there is not enough room (line full)
2230+--------------------------------------------------------------------------*/
2231INLINE static bool
2232Is_There_Room_For_A_Char_In_Line(FORM *form)
2233{
2234  int last_char_in_line;
2235
2236  wmove(form->w, form->currow, form->current->dcols - 1);
2237  last_char_in_line = (int)(winch(form->w) & A_CHARTEXT);
2238  wmove(form->w, form->currow, form->curcol);
2239  return (((last_char_in_line == form->current->pad) ||
2240	   is_blank(last_char_in_line)) ? TRUE : FALSE);
2241}
2242
2243#define There_Is_No_Room_For_A_Char_In_Line(f) \
2244  !Is_There_Room_For_A_Char_In_Line(f)
2245
2246/*---------------------------------------------------------------------------
2247|   Facility      :  libnform
2248|   Function      :  static int Insert_String(
2249|                                             FORM * form,
2250|                                             int row,
2251|                                             char *txt,
2252|                                             int  len )
2253|
2254|   Description   :  Insert the 'len' characters beginning at pointer 'txt'
2255|                    into the 'row' of the 'form'. The insertion occurs
2256|                    on the beginning of the row, all other characters are
2257|                    moved to the right. After the text a pad character will
2258|                    be inserted to separate the text from the rest. If
2259|                    necessary the insertion moves characters on the next
2260|                    line to make place for the requested insertion string.
2261|
2262|   Return Values :  E_OK              - success
2263|                    E_REQUEST_DENIED  -
2264|                    E_SYSTEM_ERROR    - system error
2265+--------------------------------------------------------------------------*/
2266static int
2267Insert_String(FORM *form, int row, FIELD_CELL *txt, int len)
2268{
2269  FIELD *field = form->current;
2270  FIELD_CELL *bp = Address_Of_Row_In_Buffer(field, row);
2271  int datalen = (int)(After_End_Of_Data(bp, field->dcols) - bp);
2272  int freelen = field->dcols - datalen;
2273  int requiredlen = len + 1;
2274  FIELD_CELL *split;
2275  int result = E_REQUEST_DENIED;
2276
2277  if (freelen >= requiredlen)
2278    {
2279      wmove(form->w, row, 0);
2280      myINSNSTR(form->w, txt, len);
2281      wmove(form->w, row, len);
2282      myINSNSTR(form->w, &myBLANK, 1);
2283      return E_OK;
2284    }
2285  else
2286    {
2287      /* we have to move characters on the next line. If we are on the
2288         last line this may work, if the field is growable */
2289      if ((row == (field->drows - 1)) && Growable(field))
2290	{
2291	  if (!Field_Grown(field, 1))
2292	    return (E_SYSTEM_ERROR);
2293	  /* !!!Side-Effect : might be changed due to growth!!! */
2294	  bp = Address_Of_Row_In_Buffer(field, row);
2295	}
2296
2297      if (row < (field->drows - 1))
2298	{
2299	  split =
2300	    After_Last_Whitespace_Character(bp,
2301					    (int)(Get_Start_Of_Data(bp
2302								    + field->dcols
2303								    - requiredlen,
2304								    requiredlen)
2305						  - bp));
2306	  /* split points now to the first character of the portion of the
2307	     line that must be moved to the next line */
2308	  datalen = (int)(split - bp);	/* + freelen has to stay on this line   */
2309	  freelen = field->dcols - (datalen + freelen);		/* for the next line */
2310
2311	  if ((result = Insert_String(form, row + 1, split, freelen)) == E_OK)
2312	    {
2313	      wmove(form->w, row, datalen);
2314	      wclrtoeol(form->w);
2315	      wmove(form->w, row, 0);
2316	      myINSNSTR(form->w, txt, len);
2317	      wmove(form->w, row, len);
2318	      myINSNSTR(form->w, &myBLANK, 1);
2319	      return E_OK;
2320	    }
2321	}
2322      return (result);
2323    }
2324}
2325
2326/*---------------------------------------------------------------------------
2327|   Facility      :  libnform
2328|   Function      :  static int Wrapping_Not_Necessary_Or_Wrapping_Ok(
2329|                                             FORM * form)
2330|
2331|   Description   :  If a character has been entered into a field, it may
2332|                    be that wrapping has to occur. This routine checks
2333|                    whether or not wrapping is required and if so, performs
2334|                    the wrapping.
2335|
2336|   Return Values :  E_OK              - no wrapping required or wrapping
2337|                                        was successful
2338|                    E_REQUEST_DENIED  -
2339|                    E_SYSTEM_ERROR    - some system error
2340+--------------------------------------------------------------------------*/
2341static int
2342Wrapping_Not_Necessary_Or_Wrapping_Ok(FORM *form)
2343{
2344  FIELD *field = form->current;
2345  int result = E_REQUEST_DENIED;
2346  bool Last_Row = ((field->drows - 1) == form->currow);
2347
2348  if ((field->opts & O_WRAP) &&	/* wrapping wanted     */
2349      (!Single_Line_Field(field)) &&	/* must be multi-line  */
2350      (There_Is_No_Room_For_A_Char_In_Line(form)) &&	/* line is full        */
2351      (!Last_Row || Growable(field)))	/* there are more lines */
2352    {
2353      FIELD_CELL *bp;
2354      FIELD_CELL *split;
2355      int chars_to_be_wrapped;
2356      int chars_to_remain_on_line;
2357
2358      if (Last_Row)
2359	{
2360	  /* the above logic already ensures, that in this case the field
2361	     is growable */
2362	  if (!Field_Grown(field, 1))
2363	    return E_SYSTEM_ERROR;
2364	}
2365      bp = Address_Of_Current_Row_In_Buffer(form);
2366      Window_To_Buffer(form->w, field);
2367      split = After_Last_Whitespace_Character(bp, field->dcols);
2368      /* split points to the first character of the sequence to be brought
2369         on the next line */
2370      chars_to_remain_on_line = (int)(split - bp);
2371      chars_to_be_wrapped = field->dcols - chars_to_remain_on_line;
2372      if (chars_to_remain_on_line > 0)
2373	{
2374	  if ((result = Insert_String(form, form->currow + 1, split,
2375				      chars_to_be_wrapped)) == E_OK)
2376	    {
2377	      wmove(form->w, form->currow, chars_to_remain_on_line);
2378	      wclrtoeol(form->w);
2379	      if (form->curcol >= chars_to_remain_on_line)
2380		{
2381		  form->currow++;
2382		  form->curcol -= chars_to_remain_on_line;
2383		}
2384	      return E_OK;
2385	    }
2386	}
2387      else
2388	return E_OK;
2389      if (result != E_OK)
2390	{
2391	  DeleteChar(form);
2392	  Window_To_Buffer(form->w, field);
2393	  result = E_REQUEST_DENIED;
2394	}
2395    }
2396  else
2397    result = E_OK;		/* wrapping was not necessary */
2398  return (result);
2399}
2400
2401/*----------------------------------------------------------------------------
2402  Field Editing routines
2403  --------------------------------------------------------------------------*/
2404
2405/*---------------------------------------------------------------------------
2406|   Facility      :  libnform
2407|   Function      :  static int Field_Editing(
2408|                                    int (* const fct) (FORM *),
2409|                                    FORM * form)
2410|
2411|   Description   :  Generic routine for field editing requests. The driver
2412|                    routines are only called for editable fields, the
2413|                    _WINDOW_MODIFIED flag is set if editing occurred.
2414|                    This is somewhat special due to the overload semantics
2415|                    of the NEW_LINE and DEL_PREV requests.
2416|
2417|   Return Values :  Error code from low level drivers.
2418+--------------------------------------------------------------------------*/
2419static int
2420Field_Editing(int (*const fct) (FORM *), FORM *form)
2421{
2422  int res = E_REQUEST_DENIED;
2423
2424  /* We have to deal here with the specific case of the overloaded
2425     behavior of New_Line and Delete_Previous requests.
2426     They may end up in navigational requests if we are on the first
2427     character in a field. But navigation is also allowed on non-
2428     editable fields.
2429   */
2430  if ((fct == FE_Delete_Previous) &&
2431      (form->opts & O_BS_OVERLOAD) &&
2432      First_Position_In_Current_Field(form))
2433    {
2434      res = Inter_Field_Navigation(FN_Previous_Field, form);
2435    }
2436  else
2437    {
2438      if (fct == FE_New_Line)
2439	{
2440	  if ((form->opts & O_NL_OVERLOAD) &&
2441	      First_Position_In_Current_Field(form))
2442	    {
2443	      res = Inter_Field_Navigation(FN_Next_Field, form);
2444	    }
2445	  else
2446	    /* FE_New_Line deals itself with the _WINDOW_MODIFIED flag */
2447	    res = fct(form);
2448	}
2449      else
2450	{
2451	  /* From now on, everything must be editable */
2452	  if (form->current->opts & O_EDIT)
2453	    {
2454	      res = fct(form);
2455	      if (res == E_OK)
2456		form->status |= _WINDOW_MODIFIED;
2457	    }
2458	}
2459    }
2460  return res;
2461}
2462
2463/*---------------------------------------------------------------------------
2464|   Facility      :  libnform
2465|   Function      :  static int FE_New_Line(FORM * form)
2466|
2467|   Description   :  Perform a new line request. This is rather complex
2468|                    compared to other routines in this code due to the
2469|                    rather difficult to understand description in the
2470|                    manuals.
2471|
2472|   Return Values :  E_OK               - success
2473|                    E_REQUEST_DENIED   - new line not allowed
2474|                    E_SYSTEM_ERROR     - system error
2475+--------------------------------------------------------------------------*/
2476static int
2477FE_New_Line(FORM *form)
2478{
2479  FIELD *field = form->current;
2480  FIELD_CELL *bp, *t;
2481  bool Last_Row = ((field->drows - 1) == form->currow);
2482
2483  T((T_CALLED("FE_New_Line(%p)"), form));
2484  if (form->status & _OVLMODE)
2485    {
2486      if (Last_Row &&
2487	  (!(Growable(field) && !Single_Line_Field(field))))
2488	{
2489	  if (!(form->opts & O_NL_OVERLOAD))
2490	    returnCode(E_REQUEST_DENIED);
2491	  wmove(form->w, form->currow, form->curcol);
2492	  wclrtoeol(form->w);
2493	  /* we have to set this here, although it is also
2494	     handled in the generic routine. The reason is,
2495	     that FN_Next_Field may fail, but the form is
2496	     definitively changed */
2497	  form->status |= _WINDOW_MODIFIED;
2498	  returnCode(Inter_Field_Navigation(FN_Next_Field, form));
2499	}
2500      else
2501	{
2502	  if (Last_Row && !Field_Grown(field, 1))
2503	    {
2504	      /* N.B.: due to the logic in the 'if', LastRow==TRUE
2505	         means here that the field is growable and not
2506	         a single-line field */
2507	      returnCode(E_SYSTEM_ERROR);
2508	    }
2509	  wmove(form->w, form->currow, form->curcol);
2510	  wclrtoeol(form->w);
2511	  form->currow++;
2512	  form->curcol = 0;
2513	  form->status |= _WINDOW_MODIFIED;
2514	  returnCode(E_OK);
2515	}
2516    }
2517  else
2518    {
2519      /* Insert Mode */
2520      if (Last_Row &&
2521	  !(Growable(field) && !Single_Line_Field(field)))
2522	{
2523	  if (!(form->opts & O_NL_OVERLOAD))
2524	    returnCode(E_REQUEST_DENIED);
2525	  returnCode(Inter_Field_Navigation(FN_Next_Field, form));
2526	}
2527      else
2528	{
2529	  bool May_Do_It = !Last_Row && Is_There_Room_For_A_Line(form);
2530
2531	  if (!(May_Do_It || Growable(field)))
2532	    returnCode(E_REQUEST_DENIED);
2533	  if (!May_Do_It && !Field_Grown(field, 1))
2534	    returnCode(E_SYSTEM_ERROR);
2535
2536	  bp = Address_Of_Current_Position_In_Buffer(form);
2537	  t = After_End_Of_Data(bp, field->dcols - form->curcol);
2538	  wmove(form->w, form->currow, form->curcol);
2539	  wclrtoeol(form->w);
2540	  form->currow++;
2541	  form->curcol = 0;
2542	  wmove(form->w, form->currow, form->curcol);
2543	  winsertln(form->w);
2544	  myADDNSTR(form->w, bp, (int)(t - bp));
2545	  form->status |= _WINDOW_MODIFIED;
2546	  returnCode(E_OK);
2547	}
2548    }
2549}
2550
2551/*---------------------------------------------------------------------------
2552|   Facility      :  libnform
2553|   Function      :  static int FE_Insert_Character(FORM * form)
2554|
2555|   Description   :  Insert blank character at the cursor position
2556|
2557|   Return Values :  E_OK
2558|                    E_REQUEST_DENIED
2559+--------------------------------------------------------------------------*/
2560static int
2561FE_Insert_Character(FORM *form)
2562{
2563  FIELD *field = form->current;
2564  int result = E_REQUEST_DENIED;
2565
2566  T((T_CALLED("FE_Insert_Character(%p)"), form));
2567  if (Check_Char(field->type, (int)C_BLANK, (TypeArgument *)(field->arg)))
2568    {
2569      bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
2570
2571      if (There_Is_Room ||
2572	  ((Single_Line_Field(field) && Growable(field))))
2573	{
2574	  if (!There_Is_Room && !Field_Grown(field, 1))
2575	    result = E_SYSTEM_ERROR;
2576	  else
2577	    {
2578	      winsch(form->w, (chtype)C_BLANK);
2579	      result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form);
2580	    }
2581	}
2582    }
2583  returnCode(result);
2584}
2585
2586/*---------------------------------------------------------------------------
2587|   Facility      :  libnform
2588|   Function      :  static int FE_Insert_Line(FORM * form)
2589|
2590|   Description   :  Insert a blank line at the cursor position
2591|
2592|   Return Values :  E_OK               - success
2593|                    E_REQUEST_DENIED   - line can not be inserted
2594+--------------------------------------------------------------------------*/
2595static int
2596FE_Insert_Line(FORM *form)
2597{
2598  FIELD *field = form->current;
2599  int result = E_REQUEST_DENIED;
2600
2601  T((T_CALLED("FE_Insert_Line(%p)"), form));
2602  if (Check_Char(field->type, (int)C_BLANK, (TypeArgument *)(field->arg)))
2603    {
2604      bool Maybe_Done = (form->currow != (field->drows - 1)) &&
2605      Is_There_Room_For_A_Line(form);
2606
2607      if (!Single_Line_Field(field) &&
2608	  (Maybe_Done || Growable(field)))
2609	{
2610	  if (!Maybe_Done && !Field_Grown(field, 1))
2611	    result = E_SYSTEM_ERROR;
2612	  else
2613	    {
2614	      form->curcol = 0;
2615	      winsertln(form->w);
2616	      result = E_OK;
2617	    }
2618	}
2619    }
2620  returnCode(result);
2621}
2622
2623/*---------------------------------------------------------------------------
2624|   Facility      :  libnform
2625|   Function      :  static int FE_Delete_Character(FORM * form)
2626|
2627|   Description   :  Delete character at the cursor position
2628|
2629|   Return Values :  E_OK    - success
2630+--------------------------------------------------------------------------*/
2631static int
2632FE_Delete_Character(FORM *form)
2633{
2634  T((T_CALLED("FE_Delete_Character(%p)"), form));
2635  DeleteChar(form);
2636  returnCode(E_OK);
2637}
2638
2639/*---------------------------------------------------------------------------
2640|   Facility      :  libnform
2641|   Function      :  static int FE_Delete_Previous(FORM * form)
2642|
2643|   Description   :  Delete character before cursor. Again this is a rather
2644|                    difficult piece compared to others due to the overloading
2645|                    semantics of backspace.
2646|                    N.B.: The case of overloaded BS on first field position
2647|                          is already handled in the generic routine.
2648|
2649|   Return Values :  E_OK                - success
2650|                    E_REQUEST_DENIED    - Character can't be deleted
2651+--------------------------------------------------------------------------*/
2652static int
2653FE_Delete_Previous(FORM *form)
2654{
2655  FIELD *field = form->current;
2656
2657  T((T_CALLED("FE_Delete_Previous(%p)"), form));
2658  if (First_Position_In_Current_Field(form))
2659    returnCode(E_REQUEST_DENIED);
2660
2661  if ((--(form->curcol)) < 0)
2662    {
2663      FIELD_CELL *this_line, *prev_line, *prev_end, *this_end;
2664      int this_row = form->currow;
2665
2666      form->curcol++;
2667      if (form->status & _OVLMODE)
2668	returnCode(E_REQUEST_DENIED);
2669
2670      prev_line = Address_Of_Row_In_Buffer(field, (form->currow - 1));
2671      this_line = Address_Of_Row_In_Buffer(field, (form->currow));
2672      Synchronize_Buffer(form);
2673      prev_end = After_End_Of_Data(prev_line, field->dcols);
2674      this_end = After_End_Of_Data(this_line, field->dcols);
2675      if ((int)(this_end - this_line) >
2676	  (field->cols - (int)(prev_end - prev_line)))
2677	returnCode(E_REQUEST_DENIED);
2678      wmove(form->w, form->currow, form->curcol);
2679      wdeleteln(form->w);
2680      Adjust_Cursor_Position(form, prev_end);
2681      /*
2682       * If we did not really move to the previous line, help the user a
2683       * little.  It is however a little inconsistent.  Normally, when
2684       * backspacing around the point where text wraps to a new line in a
2685       * multi-line form, we absorb one keystroke for the wrapping point.  That
2686       * is consistent with SVr4 forms.  However, SVr4 does not allow typing
2687       * into the last column of the field, and requires the user to enter a
2688       * newline to move to the next line.  Therefore it can consistently eat
2689       * that keystroke.  Since ncurses allows the last column, it wraps
2690       * automatically (given the proper options).  But we cannot eat the
2691       * keystroke to back over the wrapping point, since that would put the
2692       * cursor past the end of the form field.  In this case, just delete the
2693       * character at the end of the field.
2694       */
2695      if (form->currow == this_row && this_row > 0)
2696	{
2697	  form->currow -= 1;
2698	  form->curcol = field->dcols - 1;
2699	  DeleteChar(form);
2700	}
2701      else
2702	{
2703	  wmove(form->w, form->currow, form->curcol);
2704	  myADDNSTR(form->w, this_line, (int)(this_end - this_line));
2705	}
2706    }
2707  else
2708    {
2709      DeleteChar(form);
2710    }
2711  returnCode(E_OK);
2712}
2713
2714/*---------------------------------------------------------------------------
2715|   Facility      :  libnform
2716|   Function      :  static int FE_Delete_Line(FORM * form)
2717|
2718|   Description   :  Delete line at cursor position.
2719|
2720|   Return Values :  E_OK  - success
2721+--------------------------------------------------------------------------*/
2722static int
2723FE_Delete_Line(FORM *form)
2724{
2725  T((T_CALLED("FE_Delete_Line(%p)"), form));
2726  form->curcol = 0;
2727  wdeleteln(form->w);
2728  returnCode(E_OK);
2729}
2730
2731/*---------------------------------------------------------------------------
2732|   Facility      :  libnform
2733|   Function      :  static int FE_Delete_Word(FORM * form)
2734|
2735|   Description   :  Delete word at cursor position
2736|
2737|   Return Values :  E_OK               - success
2738|                    E_REQUEST_DENIED   - failure
2739+--------------------------------------------------------------------------*/
2740static int
2741FE_Delete_Word(FORM *form)
2742{
2743  FIELD *field = form->current;
2744  FIELD_CELL *bp = Address_Of_Current_Row_In_Buffer(form);
2745  FIELD_CELL *ep = bp + field->dcols;
2746  FIELD_CELL *cp = bp + form->curcol;
2747  FIELD_CELL *s;
2748
2749  T((T_CALLED("FE_Delete_Word(%p)"), form));
2750  Synchronize_Buffer(form);
2751  if (ISBLANK(*cp))
2752    returnCode(E_REQUEST_DENIED);	/* not in word */
2753
2754  /* move cursor to begin of word and erase to end of screen-line */
2755  Adjust_Cursor_Position(form,
2756			 After_Last_Whitespace_Character(bp, form->curcol));
2757  wmove(form->w, form->currow, form->curcol);
2758  wclrtoeol(form->w);
2759
2760  /* skip over word in buffer */
2761  s = Get_First_Whitespace_Character(cp, (int)(ep - cp));
2762  /* to begin of next word    */
2763  s = Get_Start_Of_Data(s, (int)(ep - s));
2764  if ((s != cp) && !ISBLANK(*s))
2765    {
2766      /* copy remaining line to window */
2767      myADDNSTR(form->w, s, (int)(s - After_End_Of_Data(s, (int)(ep - s))));
2768    }
2769  returnCode(E_OK);
2770}
2771
2772/*---------------------------------------------------------------------------
2773|   Facility      :  libnform
2774|   Function      :  static int FE_Clear_To_End_Of_Line(FORM * form)
2775|
2776|   Description   :  Clear to end of current line.
2777|
2778|   Return Values :  E_OK   - success
2779+--------------------------------------------------------------------------*/
2780static int
2781FE_Clear_To_End_Of_Line(FORM *form)
2782{
2783  T((T_CALLED("FE_Clear_To_End_Of_Line(%p)"), form));
2784  wmove(form->w, form->currow, form->curcol);
2785  wclrtoeol(form->w);
2786  returnCode(E_OK);
2787}
2788
2789/*---------------------------------------------------------------------------
2790|   Facility      :  libnform
2791|   Function      :  static int FE_Clear_To_End_Of_Field(FORM * form)
2792|
2793|   Description   :  Clear to end of field.
2794|
2795|   Return Values :  E_OK   - success
2796+--------------------------------------------------------------------------*/
2797static int
2798FE_Clear_To_End_Of_Field(FORM *form)
2799{
2800  T((T_CALLED("FE_Clear_To_End_Of_Field(%p)"), form));
2801  wmove(form->w, form->currow, form->curcol);
2802  wclrtobot(form->w);
2803  returnCode(E_OK);
2804}
2805
2806/*---------------------------------------------------------------------------
2807|   Facility      :  libnform
2808|   Function      :  static int FE_Clear_Field(FORM * form)
2809|
2810|   Description   :  Clear entire field.
2811|
2812|   Return Values :  E_OK   - success
2813+--------------------------------------------------------------------------*/
2814static int
2815FE_Clear_Field(FORM *form)
2816{
2817  T((T_CALLED("FE_Clear_Field(%p)"), form));
2818  form->currow = form->curcol = 0;
2819  werase(form->w);
2820  returnCode(E_OK);
2821}
2822/*----------------------------------------------------------------------------
2823  END of Field Editing routines
2824  --------------------------------------------------------------------------*/
2825
2826/*----------------------------------------------------------------------------
2827  Edit Mode routines
2828  --------------------------------------------------------------------------*/
2829
2830/*---------------------------------------------------------------------------
2831|   Facility      :  libnform
2832|   Function      :  static int EM_Overlay_Mode(FORM * form)
2833|
2834|   Description   :  Switch to overlay mode.
2835|
2836|   Return Values :  E_OK   - success
2837+--------------------------------------------------------------------------*/
2838static int
2839EM_Overlay_Mode(FORM *form)
2840{
2841  T((T_CALLED("EM_Overlay_Mode(%p)"), form));
2842  form->status |= _OVLMODE;
2843  returnCode(E_OK);
2844}
2845
2846/*---------------------------------------------------------------------------
2847|   Facility      :  libnform
2848|   Function      :  static int EM_Insert_Mode(FORM * form)
2849|
2850|   Description   :  Switch to insert mode
2851|
2852|   Return Values :  E_OK   - success
2853+--------------------------------------------------------------------------*/
2854static int
2855EM_Insert_Mode(FORM *form)
2856{
2857  T((T_CALLED("EM_Insert_Mode(%p)"), form));
2858  form->status &= ~_OVLMODE;
2859  returnCode(E_OK);
2860}
2861
2862/*----------------------------------------------------------------------------
2863  END of Edit Mode routines
2864  --------------------------------------------------------------------------*/
2865
2866/*----------------------------------------------------------------------------
2867  Helper routines for Choice Requests
2868  --------------------------------------------------------------------------*/
2869
2870/*---------------------------------------------------------------------------
2871|   Facility      :  libnform
2872|   Function      :  static bool Next_Choice(
2873|                                            FIELDTYPE * typ,
2874|                                            FIELD * field,
2875|                                            TypeArgument *argp)
2876|
2877|   Description   :  Get the next field choice. For linked types this is
2878|                    done recursively.
2879|
2880|   Return Values :  TRUE    - next choice successfully retrieved
2881|                    FALSE   - couldn't retrieve next choice
2882+--------------------------------------------------------------------------*/
2883static bool
2884Next_Choice(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
2885{
2886  if (!typ || !(typ->status & _HAS_CHOICE))
2887    return FALSE;
2888
2889  if (typ->status & _LINKED_TYPE)
2890    {
2891      assert(argp);
2892      return (
2893	       Next_Choice(typ->left, field, argp->left) ||
2894	       Next_Choice(typ->right, field, argp->right));
2895    }
2896  else
2897    {
2898      assert(typ->next);
2899      return typ->next(field, (void *)argp);
2900    }
2901}
2902
2903/*---------------------------------------------------------------------------
2904|   Facility      :  libnform
2905|   Function      :  static bool Previous_Choice(
2906|                                                FIELDTYPE * typ,
2907|                                                FIELD * field,
2908|                                                TypeArgument *argp)
2909|
2910|   Description   :  Get the previous field choice. For linked types this
2911|                    is done recursively.
2912|
2913|   Return Values :  TRUE    - previous choice successfully retrieved
2914|                    FALSE   - couldn't retrieve previous choice
2915+--------------------------------------------------------------------------*/
2916static bool
2917Previous_Choice(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
2918{
2919  if (!typ || !(typ->status & _HAS_CHOICE))
2920    return FALSE;
2921
2922  if (typ->status & _LINKED_TYPE)
2923    {
2924      assert(argp);
2925      return (
2926	       Previous_Choice(typ->left, field, argp->left) ||
2927	       Previous_Choice(typ->right, field, argp->right));
2928    }
2929  else
2930    {
2931      assert(typ->prev);
2932      return typ->prev(field, (void *)argp);
2933    }
2934}
2935/*----------------------------------------------------------------------------
2936  End of Helper routines for Choice Requests
2937  --------------------------------------------------------------------------*/
2938
2939/*----------------------------------------------------------------------------
2940  Routines for Choice Requests
2941  --------------------------------------------------------------------------*/
2942
2943/*---------------------------------------------------------------------------
2944|   Facility      :  libnform
2945|   Function      :  static int CR_Next_Choice(FORM * form)
2946|
2947|   Description   :  Get the next field choice.
2948|
2949|   Return Values :  E_OK              - success
2950|                    E_REQUEST_DENIED  - next choice couldn't be retrieved
2951+--------------------------------------------------------------------------*/
2952static int
2953CR_Next_Choice(FORM *form)
2954{
2955  FIELD *field = form->current;
2956
2957  T((T_CALLED("CR_Next_Choice(%p)"), form));
2958  Synchronize_Buffer(form);
2959  returnCode((Next_Choice(field->type, field, (TypeArgument *)(field->arg)))
2960	     ? E_OK
2961	     : E_REQUEST_DENIED);
2962}
2963
2964/*---------------------------------------------------------------------------
2965|   Facility      :  libnform
2966|   Function      :  static int CR_Previous_Choice(FORM * form)
2967|
2968|   Description   :  Get the previous field choice.
2969|
2970|   Return Values :  E_OK              - success
2971|                    E_REQUEST_DENIED  - prev. choice couldn't be retrieved
2972+--------------------------------------------------------------------------*/
2973static int
2974CR_Previous_Choice(FORM *form)
2975{
2976  FIELD *field = form->current;
2977
2978  T((T_CALLED("CR_Previous_Choice(%p)"), form));
2979  Synchronize_Buffer(form);
2980  returnCode((Previous_Choice(field->type, field, (TypeArgument *)(field->arg)))
2981	     ? E_OK
2982	     : E_REQUEST_DENIED);
2983}
2984/*----------------------------------------------------------------------------
2985  End of Routines for Choice Requests
2986  --------------------------------------------------------------------------*/
2987
2988/*----------------------------------------------------------------------------
2989  Helper routines for Field Validations.
2990  --------------------------------------------------------------------------*/
2991
2992/*---------------------------------------------------------------------------
2993|   Facility      :  libnform
2994|   Function      :  static bool Check_Field(
2995|                                            FIELDTYPE * typ,
2996|                                            FIELD * field,
2997|                                            TypeArgument * argp)
2998|
2999|   Description   :  Check the field according to its fieldtype and its
3000|                    actual arguments. For linked fieldtypes this is done
3001|                    recursively.
3002|
3003|   Return Values :  TRUE       - field is valid
3004|                    FALSE      - field is invalid.
3005+--------------------------------------------------------------------------*/
3006static bool
3007Check_Field(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
3008{
3009  if (typ)
3010    {
3011      if (field->opts & O_NULLOK)
3012	{
3013	  FIELD_CELL *bp = field->buf;
3014
3015	  assert(bp);
3016	  while (ISBLANK(*bp))
3017	    {
3018	      bp++;
3019	    }
3020	  if (CharOf(*bp) == 0)
3021	    return TRUE;
3022	}
3023
3024      if (typ->status & _LINKED_TYPE)
3025	{
3026	  assert(argp);
3027	  return (
3028		   Check_Field(typ->left, field, argp->left) ||
3029		   Check_Field(typ->right, field, argp->right));
3030	}
3031      else
3032	{
3033	  if (typ->fcheck)
3034	    return typ->fcheck(field, (void *)argp);
3035	}
3036    }
3037  return TRUE;
3038}
3039
3040/*---------------------------------------------------------------------------
3041|   Facility      :  libnform
3042|   Function      :  bool _nc_Internal_Validation(FORM * form )
3043|
3044|   Description   :  Validate the current field of the form.
3045|
3046|   Return Values :  TRUE  - field is valid
3047|                    FALSE - field is invalid
3048+--------------------------------------------------------------------------*/
3049NCURSES_EXPORT(bool)
3050_nc_Internal_Validation(FORM *form)
3051{
3052  FIELD *field;
3053
3054  field = form->current;
3055
3056  Synchronize_Buffer(form);
3057  if ((form->status & _FCHECK_REQUIRED) ||
3058      (!(field->opts & O_PASSOK)))
3059    {
3060      if (!Check_Field(field->type, field, (TypeArgument *)(field->arg)))
3061	return FALSE;
3062      form->status &= ~_FCHECK_REQUIRED;
3063      field->status |= _CHANGED;
3064      Synchronize_Linked_Fields(field);
3065    }
3066  return TRUE;
3067}
3068/*----------------------------------------------------------------------------
3069  End of Helper routines for Field Validations.
3070  --------------------------------------------------------------------------*/
3071
3072/*----------------------------------------------------------------------------
3073  Routines for Field Validation.
3074  --------------------------------------------------------------------------*/
3075
3076/*---------------------------------------------------------------------------
3077|   Facility      :  libnform
3078|   Function      :  static int FV_Validation(FORM * form)
3079|
3080|   Description   :  Validate the current field of the form.
3081|
3082|   Return Values :  E_OK             - field valid
3083|                    E_INVALID_FIELD  - field not valid
3084+--------------------------------------------------------------------------*/
3085static int
3086FV_Validation(FORM *form)
3087{
3088  T((T_CALLED("FV_Validation(%p)"), form));
3089  if (_nc_Internal_Validation(form))
3090    returnCode(E_OK);
3091  else
3092    returnCode(E_INVALID_FIELD);
3093}
3094/*----------------------------------------------------------------------------
3095  End of routines for Field Validation.
3096  --------------------------------------------------------------------------*/
3097
3098/*----------------------------------------------------------------------------
3099  Helper routines for Inter-Field Navigation
3100  --------------------------------------------------------------------------*/
3101
3102/*---------------------------------------------------------------------------
3103|   Facility      :  libnform
3104|   Function      :  static FIELD *Next_Field_On_Page(FIELD * field)
3105|
3106|   Description   :  Get the next field after the given field on the current
3107|                    page. The order of fields is the one defined by the
3108|                    fields array. Only visible and active fields are
3109|                    counted.
3110|
3111|   Return Values :  Pointer to the next field.
3112+--------------------------------------------------------------------------*/
3113INLINE static FIELD *
3114Next_Field_On_Page(FIELD *field)
3115{
3116  FORM *form = field->form;
3117  FIELD **field_on_page = &form->field[field->index];
3118  FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
3119  FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3120
3121  do
3122    {
3123      field_on_page =
3124	(field_on_page == last_on_page) ? first_on_page : field_on_page + 1;
3125      if (Field_Is_Selectable(*field_on_page))
3126	break;
3127    }
3128  while (field != (*field_on_page));
3129  return (*field_on_page);
3130}
3131
3132/*---------------------------------------------------------------------------
3133|   Facility      :  libnform
3134|   Function      :  FIELD* _nc_First_Active_Field(FORM * form)
3135|
3136|   Description   :  Get the first active field on the current page,
3137|                    if there are such. If there are none, get the first
3138|                    visible field on the page. If there are also none,
3139|                    we return the first field on page and hope the best.
3140|
3141|   Return Values :  Pointer to calculated field.
3142+--------------------------------------------------------------------------*/
3143NCURSES_EXPORT(FIELD *)
3144_nc_First_Active_Field(FORM *form)
3145{
3146  FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3147  FIELD *proposed = Next_Field_On_Page(*last_on_page);
3148
3149  if (proposed == *last_on_page)
3150    {
3151      /* there might be the special situation, where there is no
3152         active and visible field on the current page. We then select
3153         the first visible field on this readonly page
3154       */
3155      if (Field_Is_Not_Selectable(proposed))
3156	{
3157	  FIELD **field = &form->field[proposed->index];
3158	  FIELD **first = &form->field[form->page[form->curpage].pmin];
3159
3160	  do
3161	    {
3162	      field = (field == last_on_page) ? first : field + 1;
3163	      if (((*field)->opts & O_VISIBLE))
3164		break;
3165	    }
3166	  while (proposed != (*field));
3167
3168	  proposed = *field;
3169
3170	  if ((proposed == *last_on_page) && !(proposed->opts & O_VISIBLE))
3171	    {
3172	      /* This means, there is also no visible field on the page.
3173	         So we propose the first one and hope the very best...
3174	         Some very clever user has designed a readonly and invisible
3175	         page on this form.
3176	       */
3177	      proposed = *first;
3178	    }
3179	}
3180    }
3181  return (proposed);
3182}
3183
3184/*---------------------------------------------------------------------------
3185|   Facility      :  libnform
3186|   Function      :  static FIELD *Previous_Field_On_Page(FIELD * field)
3187|
3188|   Description   :  Get the previous field before the given field on the
3189|                    current page. The order of fields is the one defined by
3190|                    the fields array. Only visible and active fields are
3191|                    counted.
3192|
3193|   Return Values :  Pointer to the previous field.
3194+--------------------------------------------------------------------------*/
3195INLINE static FIELD *
3196Previous_Field_On_Page(FIELD *field)
3197{
3198  FORM *form = field->form;
3199  FIELD **field_on_page = &form->field[field->index];
3200  FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
3201  FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3202
3203  do
3204    {
3205      field_on_page =
3206	(field_on_page == first_on_page) ? last_on_page : field_on_page - 1;
3207      if (Field_Is_Selectable(*field_on_page))
3208	break;
3209    }
3210  while (field != (*field_on_page));
3211
3212  return (*field_on_page);
3213}
3214
3215/*---------------------------------------------------------------------------
3216|   Facility      :  libnform
3217|   Function      :  static FIELD *Sorted_Next_Field(FIELD * field)
3218|
3219|   Description   :  Get the next field after the given field on the current
3220|                    page. The order of fields is the one defined by the
3221|                    (row,column) geometry, rows are major.
3222|
3223|   Return Values :  Pointer to the next field.
3224+--------------------------------------------------------------------------*/
3225INLINE static FIELD *
3226Sorted_Next_Field(FIELD *field)
3227{
3228  FIELD *field_on_page = field;
3229
3230  do
3231    {
3232      field_on_page = field_on_page->snext;
3233      if (Field_Is_Selectable(field_on_page))
3234	break;
3235    }
3236  while (field_on_page != field);
3237
3238  return (field_on_page);
3239}
3240
3241/*---------------------------------------------------------------------------
3242|   Facility      :  libnform
3243|   Function      :  static FIELD *Sorted_Previous_Field(FIELD * field)
3244|
3245|   Description   :  Get the previous field before the given field on the
3246|                    current page. The order of fields is the one defined
3247|                    by the (row,column) geometry, rows are major.
3248|
3249|   Return Values :  Pointer to the previous field.
3250+--------------------------------------------------------------------------*/
3251INLINE static FIELD *
3252Sorted_Previous_Field(FIELD *field)
3253{
3254  FIELD *field_on_page = field;
3255
3256  do
3257    {
3258      field_on_page = field_on_page->sprev;
3259      if (Field_Is_Selectable(field_on_page))
3260	break;
3261    }
3262  while (field_on_page != field);
3263
3264  return (field_on_page);
3265}
3266
3267/*---------------------------------------------------------------------------
3268|   Facility      :  libnform
3269|   Function      :  static FIELD *Left_Neighbor_Field(FIELD * field)
3270|
3271|   Description   :  Get the left neighbor of the field on the same line
3272|                    and the same page. Cycles through the line.
3273|
3274|   Return Values :  Pointer to left neighbor field.
3275+--------------------------------------------------------------------------*/
3276INLINE static FIELD *
3277Left_Neighbor_Field(FIELD *field)
3278{
3279  FIELD *field_on_page = field;
3280
3281  /* For a field that has really a left neighbor, the while clause
3282     immediately fails and the loop is left, positioned at the right
3283     neighbor. Otherwise we cycle backwards through the sorted field list
3284     until we enter the same line (from the right end).
3285   */
3286  do
3287    {
3288      field_on_page = Sorted_Previous_Field(field_on_page);
3289    }
3290  while (field_on_page->frow != field->frow);
3291
3292  return (field_on_page);
3293}
3294
3295/*---------------------------------------------------------------------------
3296|   Facility      :  libnform
3297|   Function      :  static FIELD *Right_Neighbor_Field(FIELD * field)
3298|
3299|   Description   :  Get the right neighbor of the field on the same line
3300|                    and the same page.
3301|
3302|   Return Values :  Pointer to right neighbor field.
3303+--------------------------------------------------------------------------*/
3304INLINE static FIELD *
3305Right_Neighbor_Field(FIELD *field)
3306{
3307  FIELD *field_on_page = field;
3308
3309  /* See the comments on Left_Neighbor_Field to understand how it works */
3310  do
3311    {
3312      field_on_page = Sorted_Next_Field(field_on_page);
3313    }
3314  while (field_on_page->frow != field->frow);
3315
3316  return (field_on_page);
3317}
3318
3319/*---------------------------------------------------------------------------
3320|   Facility      :  libnform
3321|   Function      :  static FIELD *Upper_Neighbor_Field(FIELD * field)
3322|
3323|   Description   :  Because of the row-major nature of sorting the fields,
3324|                    it is more difficult to define whats the upper neighbor
3325|                    field really means. We define that it must be on a
3326|                    'previous' line (cyclic order!) and is the rightmost
3327|                    field laying on the left side of the given field. If
3328|                    this set is empty, we take the first field on the line.
3329|
3330|   Return Values :  Pointer to the upper neighbor field.
3331+--------------------------------------------------------------------------*/
3332static FIELD *
3333Upper_Neighbor_Field(FIELD *field)
3334{
3335  FIELD *field_on_page = field;
3336  int frow = field->frow;
3337  int fcol = field->fcol;
3338
3339  /* Walk back to the 'previous' line. The second term in the while clause
3340     just guarantees that we stop if we cycled through the line because
3341     there might be no 'previous' line if the page has just one line.
3342   */
3343  do
3344    {
3345      field_on_page = Sorted_Previous_Field(field_on_page);
3346    }
3347  while (field_on_page->frow == frow && field_on_page->fcol != fcol);
3348
3349  if (field_on_page->frow != frow)
3350    {
3351      /* We really found a 'previous' line. We are positioned at the
3352         rightmost field on this line */
3353      frow = field_on_page->frow;
3354
3355      /* We walk to the left as long as we are really right of the
3356         field. */
3357      while (field_on_page->frow == frow && field_on_page->fcol > fcol)
3358	field_on_page = Sorted_Previous_Field(field_on_page);
3359
3360      /* If we wrapped, just go to the right which is the first field on
3361         the row */
3362      if (field_on_page->frow != frow)
3363	field_on_page = Sorted_Next_Field(field_on_page);
3364    }
3365
3366  return (field_on_page);
3367}
3368
3369/*---------------------------------------------------------------------------
3370|   Facility      :  libnform
3371|   Function      :  static FIELD *Down_Neighbor_Field(FIELD * field)
3372|
3373|   Description   :  Because of the row-major nature of sorting the fields,
3374|                    its more difficult to define whats the down neighbor
3375|                    field really means. We define that it must be on a
3376|                    'next' line (cyclic order!) and is the leftmost
3377|                    field laying on the right side of the given field. If
3378|                    this set is empty, we take the last field on the line.
3379|
3380|   Return Values :  Pointer to the upper neighbor field.
3381+--------------------------------------------------------------------------*/
3382static FIELD *
3383Down_Neighbor_Field(FIELD *field)
3384{
3385  FIELD *field_on_page = field;
3386  int frow = field->frow;
3387  int fcol = field->fcol;
3388
3389  /* Walk forward to the 'next' line. The second term in the while clause
3390     just guarantees that we stop if we cycled through the line because
3391     there might be no 'next' line if the page has just one line.
3392   */
3393  do
3394    {
3395      field_on_page = Sorted_Next_Field(field_on_page);
3396    }
3397  while (field_on_page->frow == frow && field_on_page->fcol != fcol);
3398
3399  if (field_on_page->frow != frow)
3400    {
3401      /* We really found a 'next' line. We are positioned at the rightmost
3402         field on this line */
3403      frow = field_on_page->frow;
3404
3405      /* We walk to the right as long as we are really left of the
3406         field. */
3407      while (field_on_page->frow == frow && field_on_page->fcol < fcol)
3408	field_on_page = Sorted_Next_Field(field_on_page);
3409
3410      /* If we wrapped, just go to the left which is the last field on
3411         the row */
3412      if (field_on_page->frow != frow)
3413	field_on_page = Sorted_Previous_Field(field_on_page);
3414    }
3415
3416  return (field_on_page);
3417}
3418
3419/*----------------------------------------------------------------------------
3420  Inter-Field Navigation routines
3421  --------------------------------------------------------------------------*/
3422
3423/*---------------------------------------------------------------------------
3424|   Facility      :  libnform
3425|   Function      :  static int Inter_Field_Navigation(
3426|                                           int (* const fct) (FORM *),
3427|                                           FORM * form)
3428|
3429|   Description   :  Generic behavior for changing the current field, the
3430|                    field is left and a new field is entered. So the field
3431|                    must be validated and the field init/term hooks must
3432|                    be called.
3433|
3434|   Return Values :  E_OK                - success
3435|                    E_INVALID_FIELD     - field is invalid
3436|                    some other          - error from subordinate call
3437+--------------------------------------------------------------------------*/
3438static int
3439Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form)
3440{
3441  int res;
3442
3443  if (!_nc_Internal_Validation(form))
3444    res = E_INVALID_FIELD;
3445  else
3446    {
3447      Call_Hook(form, fieldterm);
3448      res = fct(form);
3449      Call_Hook(form, fieldinit);
3450    }
3451  return res;
3452}
3453
3454/*---------------------------------------------------------------------------
3455|   Facility      :  libnform
3456|   Function      :  static int FN_Next_Field(FORM * form)
3457|
3458|   Description   :  Move to the next field on the current page of the form
3459|
3460|   Return Values :  E_OK                 - success
3461|                    != E_OK              - error from subordinate call
3462+--------------------------------------------------------------------------*/
3463static int
3464FN_Next_Field(FORM *form)
3465{
3466  T((T_CALLED("FN_Next_Field(%p)"), form));
3467  returnCode(_nc_Set_Current_Field(form,
3468				   Next_Field_On_Page(form->current)));
3469}
3470
3471/*---------------------------------------------------------------------------
3472|   Facility      :  libnform
3473|   Function      :  static int FN_Previous_Field(FORM * form)
3474|
3475|   Description   :  Move to the previous field on the current page of the
3476|                    form
3477|
3478|   Return Values :  E_OK                 - success
3479|                    != E_OK              - error from subordinate call
3480+--------------------------------------------------------------------------*/
3481static int
3482FN_Previous_Field(FORM *form)
3483{
3484  T((T_CALLED("FN_Previous_Field(%p)"), form));
3485  returnCode(_nc_Set_Current_Field(form,
3486				   Previous_Field_On_Page(form->current)));
3487}
3488
3489/*---------------------------------------------------------------------------
3490|   Facility      :  libnform
3491|   Function      :  static int FN_First_Field(FORM * form)
3492|
3493|   Description   :  Move to the first field on the current page of the form
3494|
3495|   Return Values :  E_OK                 - success
3496|                    != E_OK              - error from subordinate call
3497+--------------------------------------------------------------------------*/
3498static int
3499FN_First_Field(FORM *form)
3500{
3501  T((T_CALLED("FN_First_Field(%p)"), form));
3502  returnCode(_nc_Set_Current_Field(form,
3503				   Next_Field_On_Page(form->field[form->page[form->curpage].pmax])));
3504}
3505
3506/*---------------------------------------------------------------------------
3507|   Facility      :  libnform
3508|   Function      :  static int FN_Last_Field(FORM * form)
3509|
3510|   Description   :  Move to the last field on the current page of the form
3511|
3512|   Return Values :  E_OK                 - success
3513|                    != E_OK              - error from subordinate call
3514+--------------------------------------------------------------------------*/
3515static int
3516FN_Last_Field(FORM *form)
3517{
3518  T((T_CALLED("FN_Last_Field(%p)"), form));
3519  returnCode(
3520	      _nc_Set_Current_Field(form,
3521				    Previous_Field_On_Page(form->field[form->page[form->curpage].pmin])));
3522}
3523
3524/*---------------------------------------------------------------------------
3525|   Facility      :  libnform
3526|   Function      :  static int FN_Sorted_Next_Field(FORM * form)
3527|
3528|   Description   :  Move to the sorted next field on the current page
3529|                    of the form.
3530|
3531|   Return Values :  E_OK            - success
3532|                    != E_OK         - error from subordinate call
3533+--------------------------------------------------------------------------*/
3534static int
3535FN_Sorted_Next_Field(FORM *form)
3536{
3537  T((T_CALLED("FN_Sorted_Next_Field(%p)"), form));
3538  returnCode(_nc_Set_Current_Field(form,
3539				   Sorted_Next_Field(form->current)));
3540}
3541
3542/*---------------------------------------------------------------------------
3543|   Facility      :  libnform
3544|   Function      :  static int FN_Sorted_Previous_Field(FORM * form)
3545|
3546|   Description   :  Move to the sorted previous field on the current page
3547|                    of the form.
3548|
3549|   Return Values :  E_OK            - success
3550|                    != E_OK         - error from subordinate call
3551+--------------------------------------------------------------------------*/
3552static int
3553FN_Sorted_Previous_Field(FORM *form)
3554{
3555  T((T_CALLED("FN_Sorted_Previous_Field(%p)"), form));
3556  returnCode(_nc_Set_Current_Field(form,
3557				   Sorted_Previous_Field(form->current)));
3558}
3559
3560/*---------------------------------------------------------------------------
3561|   Facility      :  libnform
3562|   Function      :  static int FN_Sorted_First_Field(FORM * form)
3563|
3564|   Description   :  Move to the sorted first field on the current page
3565|                    of the form.
3566|
3567|   Return Values :  E_OK            - success
3568|                    != E_OK         - error from subordinate call
3569+--------------------------------------------------------------------------*/
3570static int
3571FN_Sorted_First_Field(FORM *form)
3572{
3573  T((T_CALLED("FN_Sorted_First_Field(%p)"), form));
3574  returnCode(_nc_Set_Current_Field(form,
3575				   Sorted_Next_Field(form->field[form->page[form->curpage].smax])));
3576}
3577
3578/*---------------------------------------------------------------------------
3579|   Facility      :  libnform
3580|   Function      :  static int FN_Sorted_Last_Field(FORM * form)
3581|
3582|   Description   :  Move to the sorted last field on the current page
3583|                    of the form.
3584|
3585|   Return Values :  E_OK            - success
3586|                    != E_OK         - error from subordinate call
3587+--------------------------------------------------------------------------*/
3588static int
3589FN_Sorted_Last_Field(FORM *form)
3590{
3591  T((T_CALLED("FN_Sorted_Last_Field(%p)"), form));
3592  returnCode(_nc_Set_Current_Field(form,
3593				   Sorted_Previous_Field(form->field[form->page[form->curpage].smin])));
3594}
3595
3596/*---------------------------------------------------------------------------
3597|   Facility      :  libnform
3598|   Function      :  static int FN_Left_Field(FORM * form)
3599|
3600|   Description   :  Get the field on the left of the current field on the
3601|                    same line and the same page. Cycles through the line.
3602|
3603|   Return Values :  E_OK            - success
3604|                    != E_OK         - error from subordinate call
3605+--------------------------------------------------------------------------*/
3606static int
3607FN_Left_Field(FORM *form)
3608{
3609  T((T_CALLED("FN_Left_Field(%p)"), form));
3610  returnCode(_nc_Set_Current_Field(form,
3611				   Left_Neighbor_Field(form->current)));
3612}
3613
3614/*---------------------------------------------------------------------------
3615|   Facility      :  libnform
3616|   Function      :  static int FN_Right_Field(FORM * form)
3617|
3618|   Description   :  Get the field on the right of the current field on the
3619|                    same line and the same page. Cycles through the line.
3620|
3621|   Return Values :  E_OK            - success
3622|                    != E_OK         - error from subordinate call
3623+--------------------------------------------------------------------------*/
3624static int
3625FN_Right_Field(FORM *form)
3626{
3627  T((T_CALLED("FN_Right_Field(%p)"), form));
3628  returnCode(_nc_Set_Current_Field(form,
3629				   Right_Neighbor_Field(form->current)));
3630}
3631
3632/*---------------------------------------------------------------------------
3633|   Facility      :  libnform
3634|   Function      :  static int FN_Up_Field(FORM * form)
3635|
3636|   Description   :  Get the upper neighbor of the current field. This
3637|                    cycles through the page. See the comments of the
3638|                    Upper_Neighbor_Field function to understand how
3639|                    'upper' is defined.
3640|
3641|   Return Values :  E_OK            - success
3642|                    != E_OK         - error from subordinate call
3643+--------------------------------------------------------------------------*/
3644static int
3645FN_Up_Field(FORM *form)
3646{
3647  T((T_CALLED("FN_Up_Field(%p)"), form));
3648  returnCode(_nc_Set_Current_Field(form,
3649				   Upper_Neighbor_Field(form->current)));
3650}
3651
3652/*---------------------------------------------------------------------------
3653|   Facility      :  libnform
3654|   Function      :  static int FN_Down_Field(FORM * form)
3655|
3656|   Description   :  Get the down neighbor of the current field. This
3657|                    cycles through the page. See the comments of the
3658|                    Down_Neighbor_Field function to understand how
3659|                    'down' is defined.
3660|
3661|   Return Values :  E_OK            - success
3662|                    != E_OK         - error from subordinate call
3663+--------------------------------------------------------------------------*/
3664static int
3665FN_Down_Field(FORM *form)
3666{
3667  T((T_CALLED("FN_Down_Field(%p)"), form));
3668  returnCode(_nc_Set_Current_Field(form,
3669				   Down_Neighbor_Field(form->current)));
3670}
3671/*----------------------------------------------------------------------------
3672  END of Field Navigation routines
3673  --------------------------------------------------------------------------*/
3674
3675/*----------------------------------------------------------------------------
3676  Helper routines for Page Navigation
3677  --------------------------------------------------------------------------*/
3678
3679/*---------------------------------------------------------------------------
3680|   Facility      :  libnform
3681|   Function      :  int _nc_Set_Form_Page(FORM * form,
3682|                                          int page,
3683|                                          FIELD * field)
3684|
3685|   Description   :  Make the given page number the current page and make
3686|                    the given field the current field on the page. If
3687|                    for the field NULL is given, make the first field on
3688|                    the page the current field. The routine acts only
3689|                    if the requested page is not the current page.
3690|
3691|   Return Values :  E_OK                - success
3692|                    != E_OK             - error from subordinate call
3693+--------------------------------------------------------------------------*/
3694NCURSES_EXPORT(int)
3695_nc_Set_Form_Page(FORM *form, int page, FIELD *field)
3696{
3697  int res = E_OK;
3698
3699  if ((form->curpage != page))
3700    {
3701      FIELD *last_field, *field_on_page;
3702
3703      werase(Get_Form_Window(form));
3704      form->curpage = page;
3705      last_field = field_on_page = form->field[form->page[page].smin];
3706      do
3707	{
3708	  if (field_on_page->opts & O_VISIBLE)
3709	    if ((res = Display_Field(field_on_page)) != E_OK)
3710	      return (res);
3711	  field_on_page = field_on_page->snext;
3712	}
3713      while (field_on_page != last_field);
3714
3715      if (field)
3716	res = _nc_Set_Current_Field(form, field);
3717      else
3718	/* N.B.: we don't encapsulate this by Inter_Field_Navigation(),
3719	   because this is already executed in a page navigation
3720	   context that contains field navigation
3721	 */
3722	res = FN_First_Field(form);
3723    }
3724  return (res);
3725}
3726
3727/*---------------------------------------------------------------------------
3728|   Facility      :  libnform
3729|   Function      :  static int Next_Page_Number(const FORM * form)
3730|
3731|   Description   :  Calculate the page number following the current page
3732|                    number. This cycles if the highest page number is
3733|                    reached.
3734|
3735|   Return Values :  The next page number
3736+--------------------------------------------------------------------------*/
3737INLINE static int
3738Next_Page_Number(const FORM *form)
3739{
3740  return (form->curpage + 1) % form->maxpage;
3741}
3742
3743/*---------------------------------------------------------------------------
3744|   Facility      :  libnform
3745|   Function      :  static int Previous_Page_Number(const FORM * form)
3746|
3747|   Description   :  Calculate the page number before the current page
3748|                    number. This cycles if the first page number is
3749|                    reached.
3750|
3751|   Return Values :  The previous page number
3752+--------------------------------------------------------------------------*/
3753INLINE static int
3754Previous_Page_Number(const FORM *form)
3755{
3756  return (form->curpage != 0 ? form->curpage - 1 : form->maxpage - 1);
3757}
3758
3759/*----------------------------------------------------------------------------
3760  Page Navigation routines
3761  --------------------------------------------------------------------------*/
3762
3763/*---------------------------------------------------------------------------
3764|   Facility      :  libnform
3765|   Function      :  static int Page_Navigation(
3766|                                               int (* const fct) (FORM *),
3767|                                               FORM * form)
3768|
3769|   Description   :  Generic behavior for changing a page. This means
3770|                    that the field is left and a new field is entered.
3771|                    So the field must be validated and the field init/term
3772|                    hooks must be called. Because also the page is changed,
3773|                    the forms init/term hooks must be called also.
3774|
3775|   Return Values :  E_OK                - success
3776|                    E_INVALID_FIELD     - field is invalid
3777|                    some other          - error from subordinate call
3778+--------------------------------------------------------------------------*/
3779static int
3780Page_Navigation(int (*const fct) (FORM *), FORM *form)
3781{
3782  int res;
3783
3784  if (!_nc_Internal_Validation(form))
3785    res = E_INVALID_FIELD;
3786  else
3787    {
3788      Call_Hook(form, fieldterm);
3789      Call_Hook(form, formterm);
3790      res = fct(form);
3791      Call_Hook(form, forminit);
3792      Call_Hook(form, fieldinit);
3793    }
3794  return res;
3795}
3796
3797/*---------------------------------------------------------------------------
3798|   Facility      :  libnform
3799|   Function      :  static int PN_Next_Page(FORM * form)
3800|
3801|   Description   :  Move to the next page of the form
3802|
3803|   Return Values :  E_OK                - success
3804|                    != E_OK             - error from subordinate call
3805+--------------------------------------------------------------------------*/
3806static int
3807PN_Next_Page(FORM *form)
3808{
3809  T((T_CALLED("PN_Next_Page(%p)"), form));
3810  returnCode(_nc_Set_Form_Page(form, Next_Page_Number(form), (FIELD *)0));
3811}
3812
3813/*---------------------------------------------------------------------------
3814|   Facility      :  libnform
3815|   Function      :  static int PN_Previous_Page(FORM * form)
3816|
3817|   Description   :  Move to the previous page of the form
3818|
3819|   Return Values :  E_OK              - success
3820|                    != E_OK           - error from subordinate call
3821+--------------------------------------------------------------------------*/
3822static int
3823PN_Previous_Page(FORM *form)
3824{
3825  T((T_CALLED("PN_Previous_Page(%p)"), form));
3826  returnCode(_nc_Set_Form_Page(form, Previous_Page_Number(form), (FIELD *)0));
3827}
3828
3829/*---------------------------------------------------------------------------
3830|   Facility      :  libnform
3831|   Function      :  static int PN_First_Page(FORM * form)
3832|
3833|   Description   :  Move to the first page of the form
3834|
3835|   Return Values :  E_OK              - success
3836|                    != E_OK           - error from subordinate call
3837+--------------------------------------------------------------------------*/
3838static int
3839PN_First_Page(FORM *form)
3840{
3841  T((T_CALLED("PN_First_Page(%p)"), form));
3842  returnCode(_nc_Set_Form_Page(form, 0, (FIELD *)0));
3843}
3844
3845/*---------------------------------------------------------------------------
3846|   Facility      :  libnform
3847|   Function      :  static int PN_Last_Page(FORM * form)
3848|
3849|   Description   :  Move to the last page of the form
3850|
3851|   Return Values :  E_OK              - success
3852|                    != E_OK           - error from subordinate call
3853+--------------------------------------------------------------------------*/
3854static int
3855PN_Last_Page(FORM *form)
3856{
3857  T((T_CALLED("PN_Last_Page(%p)"), form));
3858  returnCode(_nc_Set_Form_Page(form, form->maxpage - 1, (FIELD *)0));
3859}
3860
3861/*----------------------------------------------------------------------------
3862  END of Field Navigation routines
3863  --------------------------------------------------------------------------*/
3864
3865/*----------------------------------------------------------------------------
3866  Helper routines for the core form driver.
3867  --------------------------------------------------------------------------*/
3868
3869/*---------------------------------------------------------------------------
3870|   Facility      :  libnform
3871|   Function      :  static int Data_Entry(FORM * form,int c)
3872|
3873|   Description   :  Enter character c into at the current position of the
3874|                    current field of the form.
3875|
3876|   Return Values :  E_OK              -
3877|                    E_REQUEST_DENIED  -
3878|                    E_SYSTEM_ERROR    -
3879+--------------------------------------------------------------------------*/
3880static int
3881Data_Entry(FORM *form, int c)
3882{
3883  FIELD *field = form->current;
3884  int result = E_REQUEST_DENIED;
3885
3886  T((T_CALLED("Data_Entry(%p,%s)"), form, _tracechtype(c)));
3887  if ((field->opts & O_EDIT)
3888#if FIX_FORM_INACTIVE_BUG
3889      && (field->opts & O_ACTIVE)
3890#endif
3891    )
3892    {
3893      if ((field->opts & O_BLANK) &&
3894	  First_Position_In_Current_Field(form) &&
3895	  !(form->status & _FCHECK_REQUIRED) &&
3896	  !(form->status & _WINDOW_MODIFIED))
3897	werase(form->w);
3898
3899      if (form->status & _OVLMODE)
3900	{
3901	  waddch(form->w, (chtype)c);
3902	}
3903      else
3904	/* no _OVLMODE */
3905	{
3906	  bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
3907
3908	  if (!(There_Is_Room ||
3909		((Single_Line_Field(field) && Growable(field)))))
3910	    RETURN(E_REQUEST_DENIED);
3911
3912	  if (!There_Is_Room && !Field_Grown(field, 1))
3913	    RETURN(E_SYSTEM_ERROR);
3914
3915	  winsch(form->w, (chtype)c);
3916	}
3917
3918      if ((result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form)) == E_OK)
3919	{
3920	  bool End_Of_Field = (((field->drows - 1) == form->currow) &&
3921			       ((field->dcols - 1) == form->curcol));
3922
3923	  form->status |= _WINDOW_MODIFIED;
3924	  if (End_Of_Field && !Growable(field) && (field->opts & O_AUTOSKIP))
3925	    result = Inter_Field_Navigation(FN_Next_Field, form);
3926	  else
3927	    {
3928	      if (End_Of_Field && Growable(field) && !Field_Grown(field, 1))
3929		result = E_SYSTEM_ERROR;
3930	      else
3931		{
3932#if USE_WIDEC_SUPPORT
3933		  /*
3934		   * We have just added a byte to the form field.  It may have
3935		   * been part of a multibyte character.  If it was, the
3936		   * addch_used field is nonzero and we should not try to move
3937		   * to a new column.
3938		   */
3939		  if (WINDOW_EXT(form->w, addch_used) == 0)
3940		    IFN_Next_Character(form);
3941#else
3942		  IFN_Next_Character(form);
3943#endif
3944		  result = E_OK;
3945		}
3946	    }
3947	}
3948    }
3949  RETURN(result);
3950}
3951
3952/* Structure to describe the binding of a request code to a function.
3953   The member keycode codes the request value as well as the generic
3954   routine to use for the request. The code for the generic routine
3955   is coded in the upper 16 Bits while the request code is coded in
3956   the lower 16 bits.
3957
3958   In terms of C++ you might think of a request as a class with a
3959   virtual method "perform". The different types of request are
3960   derived from this base class and overload (or not) the base class
3961   implementation of perform.
3962*/
3963typedef struct
3964{
3965  int keycode;			/* must be at least 32 bit: hi:mode, lo: key */
3966  int (*cmd) (FORM *);		/* low level driver routine for this key     */
3967}
3968Binding_Info;
3969
3970/* You may see this is the class-id of the request type class */
3971#define ID_PN    (0x00000000)	/* Page navigation           */
3972#define ID_FN    (0x00010000)	/* Inter-Field navigation    */
3973#define ID_IFN   (0x00020000)	/* Intra-Field navigation    */
3974#define ID_VSC   (0x00030000)	/* Vertical Scrolling        */
3975#define ID_HSC   (0x00040000)	/* Horizontal Scrolling      */
3976#define ID_FE    (0x00050000)	/* Field Editing             */
3977#define ID_EM    (0x00060000)	/* Edit Mode                 */
3978#define ID_FV    (0x00070000)	/* Field Validation          */
3979#define ID_CH    (0x00080000)	/* Choice                    */
3980#define ID_Mask  (0xffff0000)
3981#define Key_Mask (0x0000ffff)
3982#define ID_Shft  (16)
3983
3984/* This array holds all the Binding Infos */
3985/* *INDENT-OFF* */
3986static const Binding_Info bindings[MAX_FORM_COMMAND - MIN_FORM_COMMAND + 1] =
3987{
3988  { REQ_NEXT_PAGE    |ID_PN  ,PN_Next_Page},
3989  { REQ_PREV_PAGE    |ID_PN  ,PN_Previous_Page},
3990  { REQ_FIRST_PAGE   |ID_PN  ,PN_First_Page},
3991  { REQ_LAST_PAGE    |ID_PN  ,PN_Last_Page},
3992
3993  { REQ_NEXT_FIELD   |ID_FN  ,FN_Next_Field},
3994  { REQ_PREV_FIELD   |ID_FN  ,FN_Previous_Field},
3995  { REQ_FIRST_FIELD  |ID_FN  ,FN_First_Field},
3996  { REQ_LAST_FIELD   |ID_FN  ,FN_Last_Field},
3997  { REQ_SNEXT_FIELD  |ID_FN  ,FN_Sorted_Next_Field},
3998  { REQ_SPREV_FIELD  |ID_FN  ,FN_Sorted_Previous_Field},
3999  { REQ_SFIRST_FIELD |ID_FN  ,FN_Sorted_First_Field},
4000  { REQ_SLAST_FIELD  |ID_FN  ,FN_Sorted_Last_Field},
4001  { REQ_LEFT_FIELD   |ID_FN  ,FN_Left_Field},
4002  { REQ_RIGHT_FIELD  |ID_FN  ,FN_Right_Field},
4003  { REQ_UP_FIELD     |ID_FN  ,FN_Up_Field},
4004  { REQ_DOWN_FIELD   |ID_FN  ,FN_Down_Field},
4005
4006  { REQ_NEXT_CHAR    |ID_IFN ,IFN_Next_Character},
4007  { REQ_PREV_CHAR    |ID_IFN ,IFN_Previous_Character},
4008  { REQ_NEXT_LINE    |ID_IFN ,IFN_Next_Line},
4009  { REQ_PREV_LINE    |ID_IFN ,IFN_Previous_Line},
4010  { REQ_NEXT_WORD    |ID_IFN ,IFN_Next_Word},
4011  { REQ_PREV_WORD    |ID_IFN ,IFN_Previous_Word},
4012  { REQ_BEG_FIELD    |ID_IFN ,IFN_Beginning_Of_Field},
4013  { REQ_END_FIELD    |ID_IFN ,IFN_End_Of_Field},
4014  { REQ_BEG_LINE     |ID_IFN ,IFN_Beginning_Of_Line},
4015  { REQ_END_LINE     |ID_IFN ,IFN_End_Of_Line},
4016  { REQ_LEFT_CHAR    |ID_IFN ,IFN_Left_Character},
4017  { REQ_RIGHT_CHAR   |ID_IFN ,IFN_Right_Character},
4018  { REQ_UP_CHAR      |ID_IFN ,IFN_Up_Character},
4019  { REQ_DOWN_CHAR    |ID_IFN ,IFN_Down_Character},
4020
4021  { REQ_NEW_LINE     |ID_FE  ,FE_New_Line},
4022  { REQ_INS_CHAR     |ID_FE  ,FE_Insert_Character},
4023  { REQ_INS_LINE     |ID_FE  ,FE_Insert_Line},
4024  { REQ_DEL_CHAR     |ID_FE  ,FE_Delete_Character},
4025  { REQ_DEL_PREV     |ID_FE  ,FE_Delete_Previous},
4026  { REQ_DEL_LINE     |ID_FE  ,FE_Delete_Line},
4027  { REQ_DEL_WORD     |ID_FE  ,FE_Delete_Word},
4028  { REQ_CLR_EOL      |ID_FE  ,FE_Clear_To_End_Of_Line},
4029  { REQ_CLR_EOF      |ID_FE  ,FE_Clear_To_End_Of_Field},
4030  { REQ_CLR_FIELD    |ID_FE  ,FE_Clear_Field},
4031
4032  { REQ_OVL_MODE     |ID_EM  ,EM_Overlay_Mode},
4033  { REQ_INS_MODE     |ID_EM  ,EM_Insert_Mode},
4034
4035  { REQ_SCR_FLINE    |ID_VSC ,VSC_Scroll_Line_Forward},
4036  { REQ_SCR_BLINE    |ID_VSC ,VSC_Scroll_Line_Backward},
4037  { REQ_SCR_FPAGE    |ID_VSC ,VSC_Scroll_Page_Forward},
4038  { REQ_SCR_BPAGE    |ID_VSC ,VSC_Scroll_Page_Backward},
4039  { REQ_SCR_FHPAGE   |ID_VSC ,VSC_Scroll_Half_Page_Forward},
4040  { REQ_SCR_BHPAGE   |ID_VSC ,VSC_Scroll_Half_Page_Backward},
4041
4042  { REQ_SCR_FCHAR    |ID_HSC ,HSC_Scroll_Char_Forward},
4043  { REQ_SCR_BCHAR    |ID_HSC ,HSC_Scroll_Char_Backward},
4044  { REQ_SCR_HFLINE   |ID_HSC ,HSC_Horizontal_Line_Forward},
4045  { REQ_SCR_HBLINE   |ID_HSC ,HSC_Horizontal_Line_Backward},
4046  { REQ_SCR_HFHALF   |ID_HSC ,HSC_Horizontal_Half_Line_Forward},
4047  { REQ_SCR_HBHALF   |ID_HSC ,HSC_Horizontal_Half_Line_Backward},
4048
4049  { REQ_VALIDATION   |ID_FV  ,FV_Validation},
4050
4051  { REQ_NEXT_CHOICE  |ID_CH  ,CR_Next_Choice},
4052  { REQ_PREV_CHOICE  |ID_CH  ,CR_Previous_Choice}
4053};
4054/* *INDENT-ON* */
4055
4056/*---------------------------------------------------------------------------
4057|   Facility      :  libnform
4058|   Function      :  int form_driver(FORM * form,int  c)
4059|
4060|   Description   :  This is the workhorse of the forms system. It checks
4061|                    to determine whether the character c is a request or
4062|                    data. If it is a request, the form driver executes
4063|                    the request and returns the result. If it is data
4064|                    (printable character), it enters the data into the
4065|                    current position in the current field. If it is not
4066|                    recognized, the form driver assumes it is an application
4067|                    defined command and returns E_UNKNOWN_COMMAND.
4068|                    Application defined command should be defined relative
4069|                    to MAX_FORM_COMMAND, the maximum value of a request.
4070|
4071|   Return Values :  E_OK              - success
4072|                    E_SYSTEM_ERROR    - system error
4073|                    E_BAD_ARGUMENT    - an argument is incorrect
4074|                    E_NOT_POSTED      - form is not posted
4075|                    E_INVALID_FIELD   - field contents are invalid
4076|                    E_BAD_STATE       - called from inside a hook routine
4077|                    E_REQUEST_DENIED  - request failed
4078|                    E_UNKNOWN_COMMAND - command not known
4079+--------------------------------------------------------------------------*/
4080NCURSES_EXPORT(int)
4081form_driver(FORM *form, int c)
4082{
4083  const Binding_Info *BI = (Binding_Info *) 0;
4084  int res = E_UNKNOWN_COMMAND;
4085
4086  T((T_CALLED("form_driver(%p,%d)"), form, c));
4087
4088  if (!form)
4089    RETURN(E_BAD_ARGUMENT);
4090
4091  if (!(form->field))
4092    RETURN(E_NOT_CONNECTED);
4093
4094  assert(form->page);
4095
4096  if (c == FIRST_ACTIVE_MAGIC)
4097    {
4098      form->current = _nc_First_Active_Field(form);
4099      RETURN(E_OK);
4100    }
4101
4102  assert(form->current &&
4103	 form->current->buf &&
4104	 (form->current->form == form)
4105    );
4106
4107  if (form->status & _IN_DRIVER)
4108    RETURN(E_BAD_STATE);
4109
4110  if (!(form->status & _POSTED))
4111    RETURN(E_NOT_POSTED);
4112
4113  if ((c >= MIN_FORM_COMMAND && c <= MAX_FORM_COMMAND) &&
4114      ((bindings[c - MIN_FORM_COMMAND].keycode & Key_Mask) == c))
4115    BI = &(bindings[c - MIN_FORM_COMMAND]);
4116
4117  if (BI)
4118    {
4119      typedef int (*Generic_Method) (int (*const) (FORM *), FORM *);
4120      static const Generic_Method Generic_Methods[] =
4121      {
4122	Page_Navigation,	/* overloaded to call field&form hooks */
4123	Inter_Field_Navigation,	/* overloaded to call field hooks      */
4124	NULL,			/* Intra-Field is generic              */
4125	Vertical_Scrolling,	/* Overloaded to check multi-line      */
4126	Horizontal_Scrolling,	/* Overloaded to check single-line     */
4127	Field_Editing,		/* Overloaded to mark modification     */
4128	NULL,			/* Edit Mode is generic                */
4129	NULL,			/* Field Validation is generic         */
4130	NULL			/* Choice Request is generic           */
4131      };
4132      size_t nMethods = (sizeof(Generic_Methods) / sizeof(Generic_Methods[0]));
4133      size_t method = ((BI->keycode & ID_Mask) >> ID_Shft) & 0xffff;
4134
4135      if ((method >= nMethods) || !(BI->cmd))
4136	res = E_SYSTEM_ERROR;
4137      else
4138	{
4139	  Generic_Method fct = Generic_Methods[method];
4140
4141	  if (fct)
4142	    res = fct(BI->cmd, form);
4143	  else
4144	    res = (BI->cmd) (form);
4145	}
4146    }
4147  else if (!(c & (~(int)MAX_REGULAR_CHARACTER)))
4148    {
4149      /*
4150       * If we're using 8-bit characters, iscntrl+isprint cover the whole set.
4151       * But with multibyte characters, there is a third possibility, i.e.,
4152       * parts of characters that build up into printable characters which are
4153       * not considered printable.
4154       *
4155       * FIXME: the wide-character branch should also use Check_Char().
4156       */
4157#if USE_WIDEC_SUPPORT
4158      if (!iscntrl(UChar(c)))
4159#else
4160      if (isprint(UChar(c)) &&
4161	  Check_Char(form->current->type, c,
4162		     (TypeArgument *)(form->current->arg)))
4163#endif
4164	res = Data_Entry(form, c);
4165    }
4166  _nc_Refresh_Current_Field(form);
4167  RETURN(res);
4168}
4169
4170/*----------------------------------------------------------------------------
4171  Field-Buffer manipulation routines.
4172  The effects of setting a buffer are tightly coupled to the core of the form
4173  driver logic. This is especially true in the case of growable fields.
4174  So I don't separate this into a separate module.
4175  --------------------------------------------------------------------------*/
4176
4177/*---------------------------------------------------------------------------
4178|   Facility      :  libnform
4179|   Function      :  int set_field_buffer(FIELD *field,
4180|                                         int buffer, char *value)
4181|
4182|   Description   :  Set the given buffer of the field to the given value.
4183|                    Buffer 0 stores the displayed content of the field.
4184|                    For dynamic fields this may grow the fieldbuffers if
4185|                    the length of the value exceeds the current buffer
4186|                    length. For buffer 0 only printable values are allowed.
4187|                    For static fields, the value needs not to be zero ter-
4188|                    minated. It is copied up to the length of the buffer.
4189|
4190|   Return Values :  E_OK            - success
4191|                    E_BAD_ARGUMENT  - invalid argument
4192|                    E_SYSTEM_ERROR  - system error
4193+--------------------------------------------------------------------------*/
4194NCURSES_EXPORT(int)
4195set_field_buffer(FIELD *field, int buffer, const char *value)
4196{
4197  FIELD_CELL *p;
4198  int res = E_OK;
4199  unsigned int i;
4200  unsigned int len;
4201
4202#if USE_WIDEC_SUPPORT
4203  FIELD_CELL *widevalue = 0;
4204#endif
4205
4206  T((T_CALLED("set_field_buffer(%p,%d,%s)"), field, buffer, _nc_visbuf(value)));
4207
4208  if (!field || !value || ((buffer < 0) || (buffer > field->nbuf)))
4209    RETURN(E_BAD_ARGUMENT);
4210
4211  len = Buffer_Length(field);
4212
4213  if (buffer == 0)
4214    {
4215      for (i = 0; (value[i] != '\0') && (i < len); ++i)
4216	{
4217	  if (iscntrl(UChar(value[i])))
4218	    RETURN(E_BAD_ARGUMENT);
4219	}
4220    }
4221
4222  if (Growable(field))
4223    {
4224      /* for a growable field we must assume zero terminated strings, because
4225         somehow we have to detect the length of what should be copied.
4226       */
4227      unsigned int vlen = strlen(value);
4228
4229      if (vlen > len)
4230	{
4231	  if (!Field_Grown(field,
4232			   (int)(1 + (vlen - len) / ((field->rows + field->nrow)
4233						     * field->cols))))
4234	    RETURN(E_SYSTEM_ERROR);
4235
4236	  /* in this case we also have to check, whether or not the remaining
4237	     characters in value are also printable for buffer 0. */
4238	  if (buffer == 0)
4239	    {
4240	      for (i = len; i < vlen; i++)
4241		if (iscntrl(UChar(value[i])))
4242		  RETURN(E_BAD_ARGUMENT);
4243	    }
4244	  len = vlen;
4245	}
4246    }
4247
4248  p = Address_Of_Nth_Buffer(field, buffer);
4249
4250#if USE_WIDEC_SUPPORT
4251  /*
4252   * Use addstr's logic for converting a string to an array of cchar_t's.
4253   * There should be a better way, but this handles nonspacing characters
4254   * and other special cases that we really do not want to handle here.
4255   */
4256  wclear(field->working);
4257  mvwaddstr(field->working, 0, 0, value);
4258
4259  if ((widevalue = (FIELD_CELL *)calloc(len, sizeof(FIELD_CELL))) == 0)
4260    {
4261      RETURN(E_SYSTEM_ERROR);
4262    }
4263  else
4264    {
4265      mvwin_wchnstr(field->working, 0, 0, widevalue, (int)len);
4266      for (i = 0; i < len; ++i)
4267	{
4268	  if (CharEq(myZEROS, widevalue[i]))
4269	    {
4270	      while (i < len)
4271		p[i++] = myBLANK;
4272	      break;
4273	    }
4274	  p[i] = widevalue[i];
4275	}
4276      free(widevalue);
4277    }
4278#else
4279  for (i = 0; i < len; ++i)
4280    {
4281      if (value[i] == '\0')
4282	{
4283	  while (i < len)
4284	    p[i++] = myBLANK;
4285	  break;
4286	}
4287      p[i] = value[i];
4288    }
4289#endif
4290
4291  if (buffer == 0)
4292    {
4293      int syncres;
4294
4295      if (((syncres = Synchronize_Field(field)) != E_OK) &&
4296	  (res == E_OK))
4297	res = syncres;
4298      if (((syncres = Synchronize_Linked_Fields(field)) != E_OK) &&
4299	  (res == E_OK))
4300	res = syncres;
4301    }
4302  RETURN(res);
4303}
4304
4305/*---------------------------------------------------------------------------
4306|   Facility      :  libnform
4307|   Function      :  char *field_buffer(const FIELD *field,int buffer)
4308|
4309|   Description   :  Return the address of the buffer for the field.
4310|
4311|   Return Values :  Pointer to buffer or NULL if arguments were invalid.
4312+--------------------------------------------------------------------------*/
4313NCURSES_EXPORT(char *)
4314field_buffer(const FIELD *field, int buffer)
4315{
4316  char *result = 0;
4317
4318  T((T_CALLED("field_buffer(%p,%d)"), field, buffer));
4319
4320  if (field && (buffer >= 0) && (buffer <= field->nbuf))
4321    {
4322#if USE_WIDEC_SUPPORT
4323      FIELD_CELL *data = Address_Of_Nth_Buffer(field, buffer);
4324      unsigned need = 0;
4325      int size = Buffer_Length(field);
4326      int n;
4327
4328      /* determine the number of bytes needed to store the expanded string */
4329      for (n = 0; n < size; ++n)
4330	{
4331	  if (!isWidecExt(data[n]))
4332	    {
4333	      mbstate_t state;
4334	      size_t next;
4335
4336	      init_mb(state);
4337	      next = _nc_wcrtomb(0, data[n].chars[0], &state);
4338	      if (!isEILSEQ(next))
4339		{
4340		  if (next != 0)
4341		    need += next;
4342		}
4343	    }
4344	}
4345
4346      /* allocate a place to store the expanded string */
4347      if (field->expanded[buffer] != 0)
4348	free(field->expanded[buffer]);
4349      field->expanded[buffer] = typeMalloc(char, need + 1);
4350
4351      /* expand the multibyte data */
4352      if ((result = field->expanded[buffer]) != 0)
4353	{
4354	  wclear(field->working);
4355	  mvwadd_wchnstr(field->working, 0, 0, data, size);
4356	  mvwinnstr(field->working, 0, 0, result, (int)need + 1);
4357	}
4358#else
4359      result = Address_Of_Nth_Buffer(field, buffer);
4360#endif
4361    }
4362  returnPtr(result);
4363}
4364
4365#if USE_WIDEC_SUPPORT
4366
4367/* FIXME: see lib_get_wch.c */
4368#if HAVE_MBTOWC && HAVE_MBLEN
4369#define reset_mbytes(state) mblen(NULL, 0), mbtowc(NULL, NULL, 0)
4370#define count_mbytes(buffer,length,state) mblen(buffer,length)
4371#define trans_mbytes(wch,buffer,length,state) \
4372	(int) mbtowc(&wch, buffer, length)
4373#elif HAVE_MBRTOWC && HAVE_MBRLEN
4374#define NEED_STATE
4375#define reset_mbytes(state) init_mb(state)
4376#define count_mbytes(buffer,length,state) mbrlen(buffer,length,&state)
4377#define trans_mbytes(wch,buffer,length,state) \
4378	(int) mbrtowc(&wch, buffer, length, &state)
4379#else
4380make an error
4381#endif
4382
4383/*---------------------------------------------------------------------------
4384| Convert a multibyte string to a wide-character string.  The result must be
4385| freed by the caller.
4386+--------------------------------------------------------------------------*/
4387NCURSES_EXPORT(wchar_t *)
4388_nc_Widen_String(char *source, int *lengthp)
4389{
4390  wchar_t *result = 0;
4391  wchar_t wch;
4392  size_t given = strlen(source);
4393  size_t tries;
4394  int pass;
4395  int status;
4396
4397#ifdef NEED_STATE
4398  mbstate_t state;
4399#endif
4400
4401  for (pass = 0; pass < 2; ++pass)
4402    {
4403      unsigned need = 0;
4404      size_t passed = 0;
4405
4406      while (passed < given)
4407	{
4408	  bool found = FALSE;
4409
4410	  for (tries = 1, status = 0; tries <= (given - passed); ++tries)
4411	    {
4412	      int save = source[passed + tries];
4413
4414	      source[passed + tries] = 0;
4415	      reset_mbytes(state);
4416	      status = trans_mbytes(wch, source + passed, tries, state);
4417	      source[passed + tries] = save;
4418
4419	      if (status > 0)
4420		{
4421		  found = TRUE;
4422		  break;
4423		}
4424	    }
4425	  if (found)
4426	    {
4427	      if (pass)
4428		{
4429		  result[need] = wch;
4430		}
4431	      passed += status;
4432	      ++need;
4433	    }
4434	  else
4435	    {
4436	      if (pass)
4437		{
4438		  result[need] = source[passed];
4439		}
4440	      ++need;
4441	      ++passed;
4442	    }
4443	}
4444
4445      if (!pass)
4446	{
4447	  if (!need)
4448	    break;
4449	  result = typeCalloc(wchar_t, need);
4450
4451	  *lengthp = need;
4452	  if (result == 0)
4453	    break;
4454	}
4455    }
4456
4457  return result;
4458}
4459#endif
4460
4461/* frm_driver.c ends here */
4462