xref: /freebsd/contrib/ncurses/form/frm_driver.c (revision 9336e0699bda8a301cd2bfa37106b6ec5e32012e)
1 /****************************************************************************
2  * Copyright (c) 1998-2005,2006 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 
35 MODULE_ID("$Id: frm_driver.c,v 1.76 2006/11/04 18:45:35 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 /*
81 Some options that may effect compatibility in behavior to SVr4 forms,
82 but they are here to allow a more intuitive and user friendly behavior of
83 our form implementation. This doesn't affect the API, so we feel it is
84 uncritical.
85 
86 The initial implementation tries to stay very close with the behavior
87 of the original SVr4 implementation, although in some areas it is quite
88 clear that this isn't the most appropriate way. As far as possible this
89 sources will allow you to build a forms lib that behaves quite similar
90 to SVr4, but now and in the future we will give you better options.
91 Perhaps 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   --------------------------------------------------------------------------*/
116 static int Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form);
117 static int FN_Next_Field(FORM *form);
118 static int FN_Previous_Field(FORM *form);
119 static int FE_New_Line(FORM *);
120 static 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   --------------------------------------------------------------------------*/
211 static FIELD_CELL myBLANK = BLANK;
212 static FIELD_CELL myZEROS;
213 
214 #ifdef TRACE
215 static void
216 check_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 */
242 static int
243 wins_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  */
262 static int
263 fix_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  */
272 static int
273 cell_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  */
294 static int
295 cell_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  */
321 static void
322 delete_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 +--------------------------------------------------------------------------*/
349 NCURSES_INLINE static FIELD_CELL *
350 Get_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 +--------------------------------------------------------------------------*/
371 NCURSES_INLINE static FIELD_CELL *
372 After_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 +--------------------------------------------------------------------------*/
391 NCURSES_INLINE static FIELD_CELL *
392 Get_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 +--------------------------------------------------------------------------*/
413 NCURSES_INLINE static FIELD_CELL *
414 After_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 +--------------------------------------------------------------------------*/
442 NCURSES_INLINE static void
443 Adjust_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 +--------------------------------------------------------------------------*/
473 static void
474 Buffer_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 +--------------------------------------------------------------------------*/
514 static void
515 Window_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 	    )
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 +--------------------------------------------------------------------------*/
563 NCURSES_INLINE static void
564 Synchronize_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 +--------------------------------------------------------------------------*/
589 static bool
590 Field_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 +--------------------------------------------------------------------------*/
740 NCURSES_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 +--------------------------------------------------------------------------*/
781 NCURSES_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 +--------------------------------------------------------------------------*/
909 static void
910 Perform_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 +--------------------------------------------------------------------------*/
953 static void
954 Undo_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 +--------------------------------------------------------------------------*/
983 static bool
984 Check_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 +--------------------------------------------------------------------------*/
1017 static int
1018 Display_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, WINDOW_ATTRS(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 +--------------------------------------------------------------------------*/
1072 static int
1073 Synchronize_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 +--------------------------------------------------------------------------*/
1116 static int
1117 Synchronize_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 +--------------------------------------------------------------------------*/
1152 NCURSES_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_CURRENT           - field is the current one
1216 |                    E_SYSTEM_ERROR      - some severe basic error
1217 +--------------------------------------------------------------------------*/
1218 NCURSES_EXPORT(int)
1219 _nc_Synchronize_Options(FIELD *field, Field_Options newopts)
1220 {
1221   Field_Options oldopts;
1222   Field_Options changed_opts;
1223   FORM *form;
1224   int res = E_OK;
1225 
1226   T((T_CALLED("_nc_Synchronize_Options(%p,%#x)"), field, newopts));
1227 
1228   if (!field)
1229     returnCode(E_BAD_ARGUMENT);
1230 
1231   oldopts = field->opts;
1232   changed_opts = oldopts ^ newopts;
1233   field->opts = newopts;
1234   form = field->form;
1235 
1236   if (form)
1237     {
1238       if (form->current == field)
1239 	{
1240 	  field->opts = oldopts;
1241 	  returnCode(E_CURRENT);
1242 	}
1243 
1244       if (form->status & _POSTED)
1245 	{
1246 	  if ((form->curpage == field->page))
1247 	    {
1248 	      if (changed_opts & O_VISIBLE)
1249 		{
1250 		  if (newopts & O_VISIBLE)
1251 		    res = Display_Field(field);
1252 		  else
1253 		    res = Erase_Field(field);
1254 		}
1255 	      else
1256 		{
1257 		  if ((changed_opts & O_PUBLIC) &&
1258 		      (newopts & O_VISIBLE))
1259 		    res = Display_Field(field);
1260 		}
1261 	    }
1262 	}
1263     }
1264 
1265   if (changed_opts & O_STATIC)
1266     {
1267       bool single_line_field = Single_Line_Field(field);
1268       int res2 = E_OK;
1269 
1270       if (newopts & O_STATIC)
1271 	{
1272 	  /* the field becomes now static */
1273 	  field->status &= ~_MAY_GROW;
1274 	  /* if actually we have no hidden columns, justification may
1275 	     occur again */
1276 	  if (single_line_field &&
1277 	      (field->cols == field->dcols) &&
1278 	      (field->just != NO_JUSTIFICATION) &&
1279 	      Field_Really_Appears(field))
1280 	    {
1281 	      res2 = Display_Field(field);
1282 	    }
1283 	}
1284       else
1285 	{
1286 	  /* field is no longer static */
1287 	  if ((field->maxgrow == 0) ||
1288 	      (single_line_field && (field->dcols < field->maxgrow)) ||
1289 	      (!single_line_field && (field->drows < field->maxgrow)))
1290 	    {
1291 	      field->status |= _MAY_GROW;
1292 	      /* a field with justification now changes its behavior,
1293 	         so we must redisplay it */
1294 	      if (single_line_field &&
1295 		  (field->just != NO_JUSTIFICATION) &&
1296 		  Field_Really_Appears(field))
1297 		{
1298 		  res2 = Display_Field(field);
1299 		}
1300 	    }
1301 	}
1302       if (res2 != E_OK)
1303 	res = res2;
1304     }
1305 
1306   returnCode(res);
1307 }
1308 
1309 /*---------------------------------------------------------------------------
1310 |   Facility      :  libnform
1311 |   Function      :  int _nc_Set_Current_Field(FORM  * form,
1312 |                                              FIELD * newfield)
1313 |
1314 |   Description   :  Make the newfield the new current field.
1315 |
1316 |   Return Values :  E_OK              - success
1317 |                    E_BAD_ARGUMENT    - invalid form or field pointer
1318 |                    E_SYSTEM_ERROR    - some severe basic error
1319 |                    E_NOT_CONNECTED   - no fields are connected to the form
1320 +--------------------------------------------------------------------------*/
1321 NCURSES_EXPORT(int)
1322 _nc_Set_Current_Field(FORM *form, FIELD *newfield)
1323 {
1324   FIELD *field;
1325   WINDOW *new_window;
1326 
1327   T((T_CALLED("_nc_Set_Current_Field(%p,%p)"), form, newfield));
1328 
1329   if (!form || !newfield || !form->current || (newfield->form != form))
1330     returnCode(E_BAD_ARGUMENT);
1331 
1332   if ((form->status & _IN_DRIVER))
1333     returnCode(E_BAD_STATE);
1334 
1335   if (!(form->field))
1336     returnCode(E_NOT_CONNECTED);
1337 
1338   field = form->current;
1339 
1340   if ((field != newfield) ||
1341       !(form->status & _POSTED))
1342     {
1343       if ((form->w) &&
1344 	  (field->opts & O_VISIBLE) &&
1345 	  (field->form->curpage == field->page))
1346 	{
1347 	  _nc_Refresh_Current_Field(form);
1348 	  if (field->opts & O_PUBLIC)
1349 	    {
1350 	      if (field->drows > field->rows)
1351 		{
1352 		  if (form->toprow == 0)
1353 		    field->status &= ~_NEWTOP;
1354 		  else
1355 		    field->status |= _NEWTOP;
1356 		}
1357 	      else
1358 		{
1359 		  if (Justification_Allowed(field))
1360 		    {
1361 		      Window_To_Buffer(form->w, field);
1362 		      werase(form->w);
1363 		      Perform_Justification(field, form->w);
1364 		      wsyncup(form->w);
1365 		    }
1366 		}
1367 	    }
1368 	  delwin(form->w);
1369 	  form->w = (WINDOW *)0;
1370 	}
1371 
1372       field = newfield;
1373 
1374       if (Has_Invisible_Parts(field))
1375 	new_window = newpad(field->drows, field->dcols);
1376       else
1377 	new_window = derwin(Get_Form_Window(form),
1378 			    field->rows, field->cols, field->frow, field->fcol);
1379 
1380       if (!new_window)
1381 	returnCode(E_SYSTEM_ERROR);
1382 
1383       form->current = field;
1384 
1385       if (form->w)
1386 	delwin(form->w);
1387       form->w = new_window;
1388 
1389       form->status &= ~_WINDOW_MODIFIED;
1390       Set_Field_Window_Attributes(field, form->w);
1391 
1392       if (Has_Invisible_Parts(field))
1393 	{
1394 	  werase(form->w);
1395 	  Buffer_To_Window(field, form->w);
1396 	}
1397       else
1398 	{
1399 	  if (Justification_Allowed(field))
1400 	    {
1401 	      werase(form->w);
1402 	      Undo_Justification(field, form->w);
1403 	      wsyncup(form->w);
1404 	    }
1405 	}
1406 
1407       untouchwin(form->w);
1408     }
1409 
1410   form->currow = form->curcol = form->toprow = form->begincol = 0;
1411   returnCode(E_OK);
1412 }
1413 
1414 /*----------------------------------------------------------------------------
1415   Intra-Field Navigation routines
1416   --------------------------------------------------------------------------*/
1417 
1418 /*---------------------------------------------------------------------------
1419 |   Facility      :  libnform
1420 |   Function      :  static int IFN_Next_Character(FORM * form)
1421 |
1422 |   Description   :  Move to the next character in the field. In a multi-line
1423 |                    field this wraps at the end of the line.
1424 |
1425 |   Return Values :  E_OK                - success
1426 |                    E_REQUEST_DENIED    - at the rightmost position
1427 +--------------------------------------------------------------------------*/
1428 static int
1429 IFN_Next_Character(FORM *form)
1430 {
1431   FIELD *field = form->current;
1432   int step = myWCWIDTH(form->w, form->currow, form->curcol);
1433 
1434   T((T_CALLED("IFN_Next_Character(%p)"), form));
1435   if ((form->curcol += step) == field->dcols)
1436     {
1437       if ((++(form->currow)) == field->drows)
1438 	{
1439 #if GROW_IF_NAVIGATE
1440 	  if (!Single_Line_Field(field) && Field_Grown(field, 1))
1441 	    {
1442 	      form->curcol = 0;
1443 	      returnCode(E_OK);
1444 	    }
1445 #endif
1446 	  form->currow--;
1447 #if GROW_IF_NAVIGATE
1448 	  if (Single_Line_Field(field) && Field_Grown(field, 1))
1449 	    returnCode(E_OK);
1450 #endif
1451 	  form->curcol -= step;
1452 	  returnCode(E_REQUEST_DENIED);
1453 	}
1454       form->curcol = 0;
1455     }
1456   returnCode(E_OK);
1457 }
1458 
1459 /*---------------------------------------------------------------------------
1460 |   Facility      :  libnform
1461 |   Function      :  static int IFN_Previous_Character(FORM * form)
1462 |
1463 |   Description   :  Move to the previous character in the field. In a
1464 |                    multi-line field this wraps and the beginning of the
1465 |                    line.
1466 |
1467 |   Return Values :  E_OK                - success
1468 |                    E_REQUEST_DENIED    - at the leftmost position
1469 +--------------------------------------------------------------------------*/
1470 static int
1471 IFN_Previous_Character(FORM *form)
1472 {
1473   int amount = myWCWIDTH(form->w, form->currow, form->curcol - 1);
1474   int oldcol = form->curcol;
1475 
1476   T((T_CALLED("IFN_Previous_Character(%p)"), form));
1477   if ((form->curcol -= amount) < 0)
1478     {
1479       if ((--(form->currow)) < 0)
1480 	{
1481 	  form->currow++;
1482 	  form->curcol = oldcol;
1483 	  returnCode(E_REQUEST_DENIED);
1484 	}
1485       form->curcol = form->current->dcols - 1;
1486     }
1487   returnCode(E_OK);
1488 }
1489 
1490 /*---------------------------------------------------------------------------
1491 |   Facility      :  libnform
1492 |   Function      :  static int IFN_Next_Line(FORM * form)
1493 |
1494 |   Description   :  Move to the beginning of the next line in the field
1495 |
1496 |   Return Values :  E_OK                - success
1497 |                    E_REQUEST_DENIED    - at the last line
1498 +--------------------------------------------------------------------------*/
1499 static int
1500 IFN_Next_Line(FORM *form)
1501 {
1502   FIELD *field = form->current;
1503 
1504   T((T_CALLED("IFN_Next_Line(%p)"), form));
1505   if ((++(form->currow)) == field->drows)
1506     {
1507 #if GROW_IF_NAVIGATE
1508       if (!Single_Line_Field(field) && Field_Grown(field, 1))
1509 	returnCode(E_OK);
1510 #endif
1511       form->currow--;
1512       returnCode(E_REQUEST_DENIED);
1513     }
1514   form->curcol = 0;
1515   returnCode(E_OK);
1516 }
1517 
1518 /*---------------------------------------------------------------------------
1519 |   Facility      :  libnform
1520 |   Function      :  static int IFN_Previous_Line(FORM * form)
1521 |
1522 |   Description   :  Move to the beginning of the previous line in the field
1523 |
1524 |   Return Values :  E_OK                - success
1525 |                    E_REQUEST_DENIED    - at the first line
1526 +--------------------------------------------------------------------------*/
1527 static int
1528 IFN_Previous_Line(FORM *form)
1529 {
1530   T((T_CALLED("IFN_Previous_Line(%p)"), form));
1531   if ((--(form->currow)) < 0)
1532     {
1533       form->currow++;
1534       returnCode(E_REQUEST_DENIED);
1535     }
1536   form->curcol = 0;
1537   returnCode(E_OK);
1538 }
1539 
1540 /*---------------------------------------------------------------------------
1541 |   Facility      :  libnform
1542 |   Function      :  static int IFN_Next_Word(FORM * form)
1543 |
1544 |   Description   :  Move to the beginning of the next word in the field.
1545 |
1546 |   Return Values :  E_OK             - success
1547 |                    E_REQUEST_DENIED - there is no next word
1548 +--------------------------------------------------------------------------*/
1549 static int
1550 IFN_Next_Word(FORM *form)
1551 {
1552   FIELD *field = form->current;
1553   FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
1554   FIELD_CELL *s;
1555   FIELD_CELL *t;
1556 
1557   T((T_CALLED("IFN_Next_Word(%p)"), form));
1558 
1559   /* We really need access to the data, so we have to synchronize */
1560   Synchronize_Buffer(form);
1561 
1562   /* Go to the first whitespace after the current position (including
1563      current position). This is then the starting point to look for the
1564      next non-blank data */
1565   s = Get_First_Whitespace_Character(bp, Buffer_Length(field) -
1566 				     (int)(bp - field->buf));
1567 
1568   /* Find the start of the next word */
1569   t = Get_Start_Of_Data(s, Buffer_Length(field) -
1570 			(int)(s - field->buf));
1571 #if !FRIENDLY_PREV_NEXT_WORD
1572   if (s == t)
1573     returnCode(E_REQUEST_DENIED);
1574   else
1575 #endif
1576     {
1577       Adjust_Cursor_Position(form, t);
1578       returnCode(E_OK);
1579     }
1580 }
1581 
1582 /*---------------------------------------------------------------------------
1583 |   Facility      :  libnform
1584 |   Function      :  static int IFN_Previous_Word(FORM * form)
1585 |
1586 |   Description   :  Move to the beginning of the previous word in the field.
1587 |
1588 |   Return Values :  E_OK             - success
1589 |                    E_REQUEST_DENIED - there is no previous word
1590 +--------------------------------------------------------------------------*/
1591 static int
1592 IFN_Previous_Word(FORM *form)
1593 {
1594   FIELD *field = form->current;
1595   FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
1596   FIELD_CELL *s;
1597   FIELD_CELL *t;
1598   bool again = FALSE;
1599 
1600   T((T_CALLED("IFN_Previous_Word(%p)"), form));
1601 
1602   /* We really need access to the data, so we have to synchronize */
1603   Synchronize_Buffer(form);
1604 
1605   s = After_End_Of_Data(field->buf, (int)(bp - field->buf));
1606   /* s points now right after the last non-blank in the buffer before bp.
1607      If bp was in a word, s equals bp. In this case we must find the last
1608      whitespace in the buffer before bp and repeat the game to really find
1609      the previous word! */
1610   if (s == bp)
1611     again = TRUE;
1612 
1613   /* And next call now goes backward to look for the last whitespace
1614      before that, pointing right after this, so it points to the begin
1615      of the previous word.
1616    */
1617   t = After_Last_Whitespace_Character(field->buf, (int)(s - field->buf));
1618 #if !FRIENDLY_PREV_NEXT_WORD
1619   if (s == t)
1620     returnCode(E_REQUEST_DENIED);
1621 #endif
1622   if (again)
1623     {
1624       /* and do it again, replacing bp by t */
1625       s = After_End_Of_Data(field->buf, (int)(t - field->buf));
1626       t = After_Last_Whitespace_Character(field->buf, (int)(s - field->buf));
1627 #if !FRIENDLY_PREV_NEXT_WORD
1628       if (s == t)
1629 	returnCode(E_REQUEST_DENIED);
1630 #endif
1631     }
1632   Adjust_Cursor_Position(form, t);
1633   returnCode(E_OK);
1634 }
1635 
1636 /*---------------------------------------------------------------------------
1637 |   Facility      :  libnform
1638 |   Function      :  static int IFN_Beginning_Of_Field(FORM * form)
1639 |
1640 |   Description   :  Place the cursor at the first non-pad character in
1641 |                    the field.
1642 |
1643 |   Return Values :  E_OK             - success
1644 +--------------------------------------------------------------------------*/
1645 static int
1646 IFN_Beginning_Of_Field(FORM *form)
1647 {
1648   FIELD *field = form->current;
1649 
1650   T((T_CALLED("IFN_Beginning_Of_Field(%p)"), form));
1651   Synchronize_Buffer(form);
1652   Adjust_Cursor_Position(form,
1653 			 Get_Start_Of_Data(field->buf, Buffer_Length(field)));
1654   returnCode(E_OK);
1655 }
1656 
1657 /*---------------------------------------------------------------------------
1658 |   Facility      :  libnform
1659 |   Function      :  static int IFN_End_Of_Field(FORM * form)
1660 |
1661 |   Description   :  Place the cursor after the last non-pad character in
1662 |                    the field. If the field occupies the last position in
1663 |                    the buffer, the cursor is positioned on the last
1664 |                    character.
1665 |
1666 |   Return Values :  E_OK              - success
1667 +--------------------------------------------------------------------------*/
1668 static int
1669 IFN_End_Of_Field(FORM *form)
1670 {
1671   FIELD *field = form->current;
1672   FIELD_CELL *pos;
1673 
1674   T((T_CALLED("IFN_End_Of_Field(%p)"), form));
1675   Synchronize_Buffer(form);
1676   pos = After_End_Of_Data(field->buf, Buffer_Length(field));
1677   if (pos == (field->buf + Buffer_Length(field)))
1678     pos--;
1679   Adjust_Cursor_Position(form, pos);
1680   returnCode(E_OK);
1681 }
1682 
1683 /*---------------------------------------------------------------------------
1684 |   Facility      :  libnform
1685 |   Function      :  static int IFN_Beginning_Of_Line(FORM * form)
1686 |
1687 |   Description   :  Place the cursor on the first non-pad character in
1688 |                    the current line of the field.
1689 |
1690 |   Return Values :  E_OK         - success
1691 +--------------------------------------------------------------------------*/
1692 static int
1693 IFN_Beginning_Of_Line(FORM *form)
1694 {
1695   FIELD *field = form->current;
1696 
1697   T((T_CALLED("IFN_Beginning_Of_Line(%p)"), form));
1698   Synchronize_Buffer(form);
1699   Adjust_Cursor_Position(form,
1700 			 Get_Start_Of_Data(Address_Of_Current_Row_In_Buffer(form),
1701 					   field->dcols));
1702   returnCode(E_OK);
1703 }
1704 
1705 /*---------------------------------------------------------------------------
1706 |   Facility      :  libnform
1707 |   Function      :  static int IFN_End_Of_Line(FORM * form)
1708 |
1709 |   Description   :  Place the cursor after the last non-pad character in the
1710 |                    current line of the field. If the field occupies the
1711 |                    last column in the line, the cursor is positioned on the
1712 |                    last character of the line.
1713 |
1714 |   Return Values :  E_OK        - success
1715 +--------------------------------------------------------------------------*/
1716 static int
1717 IFN_End_Of_Line(FORM *form)
1718 {
1719   FIELD *field = form->current;
1720   FIELD_CELL *pos;
1721   FIELD_CELL *bp;
1722 
1723   T((T_CALLED("IFN_End_Of_Line(%p)"), form));
1724   Synchronize_Buffer(form);
1725   bp = Address_Of_Current_Row_In_Buffer(form);
1726   pos = After_End_Of_Data(bp, field->dcols);
1727   if (pos == (bp + field->dcols))
1728     pos--;
1729   Adjust_Cursor_Position(form, pos);
1730   returnCode(E_OK);
1731 }
1732 
1733 /*---------------------------------------------------------------------------
1734 |   Facility      :  libnform
1735 |   Function      :  static int IFN_Left_Character(FORM * form)
1736 |
1737 |   Description   :  Move one character to the left in the current line.
1738 |                    This doesn't cycle.
1739 |
1740 |   Return Values :  E_OK             - success
1741 |                    E_REQUEST_DENIED - already in first column
1742 +--------------------------------------------------------------------------*/
1743 static int
1744 IFN_Left_Character(FORM *form)
1745 {
1746   int amount = myWCWIDTH(form->w, form->currow, form->curcol - 1);
1747   int oldcol = form->curcol;
1748 
1749   T((T_CALLED("IFN_Left_Character(%p)"), form));
1750   if ((form->curcol -= amount) < 0)
1751     {
1752       form->curcol = oldcol;
1753       returnCode(E_REQUEST_DENIED);
1754     }
1755   returnCode(E_OK);
1756 }
1757 
1758 /*---------------------------------------------------------------------------
1759 |   Facility      :  libnform
1760 |   Function      :  static int IFN_Right_Character(FORM * form)
1761 |
1762 |   Description   :  Move one character to the right in the current line.
1763 |                    This doesn't cycle.
1764 |
1765 |   Return Values :  E_OK              - success
1766 |                    E_REQUEST_DENIED  - already in last column
1767 +--------------------------------------------------------------------------*/
1768 static int
1769 IFN_Right_Character(FORM *form)
1770 {
1771   int amount = myWCWIDTH(form->w, form->currow, form->curcol);
1772   int oldcol = form->curcol;
1773 
1774   T((T_CALLED("IFN_Right_Character(%p)"), form));
1775   if ((form->curcol += amount) >= form->current->dcols)
1776     {
1777 #if GROW_IF_NAVIGATE
1778       FIELD *field = form->current;
1779 
1780       if (Single_Line_Field(field) && Field_Grown(field, 1))
1781 	returnCode(E_OK);
1782 #endif
1783       form->curcol = oldcol;
1784       returnCode(E_REQUEST_DENIED);
1785     }
1786   returnCode(E_OK);
1787 }
1788 
1789 /*---------------------------------------------------------------------------
1790 |   Facility      :  libnform
1791 |   Function      :  static int IFN_Up_Character(FORM * form)
1792 |
1793 |   Description   :  Move one line up. This doesn't cycle through the lines
1794 |                    of the field.
1795 |
1796 |   Return Values :  E_OK              - success
1797 |                    E_REQUEST_DENIED  - already in last column
1798 +--------------------------------------------------------------------------*/
1799 static int
1800 IFN_Up_Character(FORM *form)
1801 {
1802   T((T_CALLED("IFN_Up_Character(%p)"), form));
1803   if ((--(form->currow)) < 0)
1804     {
1805       form->currow++;
1806       returnCode(E_REQUEST_DENIED);
1807     }
1808   returnCode(E_OK);
1809 }
1810 
1811 /*---------------------------------------------------------------------------
1812 |   Facility      :  libnform
1813 |   Function      :  static int IFN_Down_Character(FORM * form)
1814 |
1815 |   Description   :  Move one line down. This doesn't cycle through the
1816 |                    lines of the field.
1817 |
1818 |   Return Values :  E_OK              - success
1819 |                    E_REQUEST_DENIED  - already in last column
1820 +--------------------------------------------------------------------------*/
1821 static int
1822 IFN_Down_Character(FORM *form)
1823 {
1824   FIELD *field = form->current;
1825 
1826   T((T_CALLED("IFN_Down_Character(%p)"), form));
1827   if ((++(form->currow)) == field->drows)
1828     {
1829 #if GROW_IF_NAVIGATE
1830       if (!Single_Line_Field(field) && Field_Grown(field, 1))
1831 	returnCode(E_OK);
1832 #endif
1833       --(form->currow);
1834       returnCode(E_REQUEST_DENIED);
1835     }
1836   returnCode(E_OK);
1837 }
1838 /*----------------------------------------------------------------------------
1839   END of Intra-Field Navigation routines
1840   --------------------------------------------------------------------------*/
1841 
1842 /*----------------------------------------------------------------------------
1843   Vertical scrolling helper routines
1844   --------------------------------------------------------------------------*/
1845 
1846 /*---------------------------------------------------------------------------
1847 |   Facility      :  libnform
1848 |   Function      :  static int VSC_Generic(FORM *form, int nlines)
1849 |
1850 |   Description   :  Scroll multi-line field forward (nlines>0) or
1851 |                    backward (nlines<0) this many lines.
1852 |
1853 |   Return Values :  E_OK              - success
1854 |                    E_REQUEST_DENIED  - can't scroll
1855 +--------------------------------------------------------------------------*/
1856 static int
1857 VSC_Generic(FORM *form, int nlines)
1858 {
1859   FIELD *field = form->current;
1860   int res = E_REQUEST_DENIED;
1861   int rows_to_go = (nlines > 0 ? nlines : -nlines);
1862 
1863   if (nlines > 0)
1864     {
1865       if ((rows_to_go + form->toprow) > (field->drows - field->rows))
1866 	rows_to_go = (field->drows - field->rows - form->toprow);
1867 
1868       if (rows_to_go > 0)
1869 	{
1870 	  form->currow += rows_to_go;
1871 	  form->toprow += rows_to_go;
1872 	  res = E_OK;
1873 	}
1874     }
1875   else
1876     {
1877       if (rows_to_go > form->toprow)
1878 	rows_to_go = form->toprow;
1879 
1880       if (rows_to_go > 0)
1881 	{
1882 	  form->currow -= rows_to_go;
1883 	  form->toprow -= rows_to_go;
1884 	  res = E_OK;
1885 	}
1886     }
1887   return (res);
1888 }
1889 /*----------------------------------------------------------------------------
1890   End of Vertical scrolling helper routines
1891   --------------------------------------------------------------------------*/
1892 
1893 /*----------------------------------------------------------------------------
1894   Vertical scrolling routines
1895   --------------------------------------------------------------------------*/
1896 
1897 /*---------------------------------------------------------------------------
1898 |   Facility      :  libnform
1899 |   Function      :  static int Vertical_Scrolling(
1900 |                                           int (* const fct) (FORM *),
1901 |                                           FORM * form)
1902 |
1903 |   Description   :  Performs the generic vertical scrolling routines.
1904 |                    This has to check for a multi-line field and to set
1905 |                    the _NEWTOP flag if scrolling really occurred.
1906 |
1907 |   Return Values :  Propagated error code from low-level driver calls
1908 +--------------------------------------------------------------------------*/
1909 static int
1910 Vertical_Scrolling(int (*const fct) (FORM *), FORM *form)
1911 {
1912   int res = E_REQUEST_DENIED;
1913 
1914   if (!Single_Line_Field(form->current))
1915     {
1916       res = fct(form);
1917       if (res == E_OK)
1918 	form->current->status |= _NEWTOP;
1919     }
1920   return (res);
1921 }
1922 
1923 /*---------------------------------------------------------------------------
1924 |   Facility      :  libnform
1925 |   Function      :  static int VSC_Scroll_Line_Forward(FORM * form)
1926 |
1927 |   Description   :  Scroll multi-line field forward a line
1928 |
1929 |   Return Values :  E_OK                - success
1930 |                    E_REQUEST_DENIED    - no data ahead
1931 +--------------------------------------------------------------------------*/
1932 static int
1933 VSC_Scroll_Line_Forward(FORM *form)
1934 {
1935   T((T_CALLED("VSC_Scroll_Line_Forward(%p)"), form));
1936   returnCode(VSC_Generic(form, 1));
1937 }
1938 
1939 /*---------------------------------------------------------------------------
1940 |   Facility      :  libnform
1941 |   Function      :  static int VSC_Scroll_Line_Backward(FORM * form)
1942 |
1943 |   Description   :  Scroll multi-line field backward a line
1944 |
1945 |   Return Values :  E_OK                - success
1946 |                    E_REQUEST_DENIED    - no data behind
1947 +--------------------------------------------------------------------------*/
1948 static int
1949 VSC_Scroll_Line_Backward(FORM *form)
1950 {
1951   T((T_CALLED("VSC_Scroll_Line_Backward(%p)"), form));
1952   returnCode(VSC_Generic(form, -1));
1953 }
1954 
1955 /*---------------------------------------------------------------------------
1956 |   Facility      :  libnform
1957 |   Function      :  static int VSC_Scroll_Page_Forward(FORM * form)
1958 |
1959 |   Description   :  Scroll a multi-line field forward a page
1960 |
1961 |   Return Values :  E_OK              - success
1962 |                    E_REQUEST_DENIED  - no data ahead
1963 +--------------------------------------------------------------------------*/
1964 static int
1965 VSC_Scroll_Page_Forward(FORM *form)
1966 {
1967   T((T_CALLED("VSC_Scroll_Page_Forward(%p)"), form));
1968   returnCode(VSC_Generic(form, form->current->rows));
1969 }
1970 
1971 /*---------------------------------------------------------------------------
1972 |   Facility      :  libnform
1973 |   Function      :  static int VSC_Scroll_Half_Page_Forward(FORM * form)
1974 |
1975 |   Description   :  Scroll a multi-line field forward half a page
1976 |
1977 |   Return Values :  E_OK              - success
1978 |                    E_REQUEST_DENIED  - no data ahead
1979 +--------------------------------------------------------------------------*/
1980 static int
1981 VSC_Scroll_Half_Page_Forward(FORM *form)
1982 {
1983   T((T_CALLED("VSC_Scroll_Half_Page_Forward(%p)"), form));
1984   returnCode(VSC_Generic(form, (form->current->rows + 1) / 2));
1985 }
1986 
1987 /*---------------------------------------------------------------------------
1988 |   Facility      :  libnform
1989 |   Function      :  static int VSC_Scroll_Page_Backward(FORM * form)
1990 |
1991 |   Description   :  Scroll a multi-line field backward a page
1992 |
1993 |   Return Values :  E_OK              - success
1994 |                    E_REQUEST_DENIED  - no data behind
1995 +--------------------------------------------------------------------------*/
1996 static int
1997 VSC_Scroll_Page_Backward(FORM *form)
1998 {
1999   T((T_CALLED("VSC_Scroll_Page_Backward(%p)"), form));
2000   returnCode(VSC_Generic(form, -(form->current->rows)));
2001 }
2002 
2003 /*---------------------------------------------------------------------------
2004 |   Facility      :  libnform
2005 |   Function      :  static int VSC_Scroll_Half_Page_Backward(FORM * form)
2006 |
2007 |   Description   :  Scroll a multi-line field backward half a page
2008 |
2009 |   Return Values :  E_OK              - success
2010 |                    E_REQUEST_DENIED  - no data behind
2011 +--------------------------------------------------------------------------*/
2012 static int
2013 VSC_Scroll_Half_Page_Backward(FORM *form)
2014 {
2015   T((T_CALLED("VSC_Scroll_Half_Page_Backward(%p)"), form));
2016   returnCode(VSC_Generic(form, -((form->current->rows + 1) / 2)));
2017 }
2018 /*----------------------------------------------------------------------------
2019   End of Vertical scrolling routines
2020   --------------------------------------------------------------------------*/
2021 
2022 /*----------------------------------------------------------------------------
2023   Horizontal scrolling helper routines
2024   --------------------------------------------------------------------------*/
2025 
2026 /*---------------------------------------------------------------------------
2027 |   Facility      :  libnform
2028 |   Function      :  static int HSC_Generic(FORM *form, int ncolumns)
2029 |
2030 |   Description   :  Scroll single-line field forward (ncolumns>0) or
2031 |                    backward (ncolumns<0) this many columns.
2032 |
2033 |   Return Values :  E_OK              - success
2034 |                    E_REQUEST_DENIED  - can't scroll
2035 +--------------------------------------------------------------------------*/
2036 static int
2037 HSC_Generic(FORM *form, int ncolumns)
2038 {
2039   FIELD *field = form->current;
2040   int res = E_REQUEST_DENIED;
2041   int cols_to_go = (ncolumns > 0 ? ncolumns : -ncolumns);
2042 
2043   if (ncolumns > 0)
2044     {
2045       if ((cols_to_go + form->begincol) > (field->dcols - field->cols))
2046 	cols_to_go = field->dcols - field->cols - form->begincol;
2047 
2048       if (cols_to_go > 0)
2049 	{
2050 	  form->curcol += cols_to_go;
2051 	  form->begincol += cols_to_go;
2052 	  res = E_OK;
2053 	}
2054     }
2055   else
2056     {
2057       if (cols_to_go > form->begincol)
2058 	cols_to_go = form->begincol;
2059 
2060       if (cols_to_go > 0)
2061 	{
2062 	  form->curcol -= cols_to_go;
2063 	  form->begincol -= cols_to_go;
2064 	  res = E_OK;
2065 	}
2066     }
2067   return (res);
2068 }
2069 /*----------------------------------------------------------------------------
2070   End of Horizontal scrolling helper routines
2071   --------------------------------------------------------------------------*/
2072 
2073 /*----------------------------------------------------------------------------
2074   Horizontal scrolling routines
2075   --------------------------------------------------------------------------*/
2076 
2077 /*---------------------------------------------------------------------------
2078 |   Facility      :  libnform
2079 |   Function      :  static int Horizontal_Scrolling(
2080 |                                          int (* const fct) (FORM *),
2081 |                                          FORM * form)
2082 |
2083 |   Description   :  Performs the generic horizontal scrolling routines.
2084 |                    This has to check for a single-line field.
2085 |
2086 |   Return Values :  Propagated error code from low-level driver calls
2087 +--------------------------------------------------------------------------*/
2088 static int
2089 Horizontal_Scrolling(int (*const fct) (FORM *), FORM *form)
2090 {
2091   if (Single_Line_Field(form->current))
2092     return fct(form);
2093   else
2094     return (E_REQUEST_DENIED);
2095 }
2096 
2097 /*---------------------------------------------------------------------------
2098 |   Facility      :  libnform
2099 |   Function      :  static int HSC_Scroll_Char_Forward(FORM * form)
2100 |
2101 |   Description   :  Scroll single-line field forward a character
2102 |
2103 |   Return Values :  E_OK                - success
2104 |                    E_REQUEST_DENIED    - no data ahead
2105 +--------------------------------------------------------------------------*/
2106 static int
2107 HSC_Scroll_Char_Forward(FORM *form)
2108 {
2109   T((T_CALLED("HSC_Scroll_Char_Forward(%p)"), form));
2110   returnCode(HSC_Generic(form, 1));
2111 }
2112 
2113 /*---------------------------------------------------------------------------
2114 |   Facility      :  libnform
2115 |   Function      :  static int HSC_Scroll_Char_Backward(FORM * form)
2116 |
2117 |   Description   :  Scroll single-line field backward a character
2118 |
2119 |   Return Values :  E_OK                - success
2120 |                    E_REQUEST_DENIED    - no data behind
2121 +--------------------------------------------------------------------------*/
2122 static int
2123 HSC_Scroll_Char_Backward(FORM *form)
2124 {
2125   T((T_CALLED("HSC_Scroll_Char_Backward(%p)"), form));
2126   returnCode(HSC_Generic(form, -1));
2127 }
2128 
2129 /*---------------------------------------------------------------------------
2130 |   Facility      :  libnform
2131 |   Function      :  static int HSC_Horizontal_Line_Forward(FORM* form)
2132 |
2133 |   Description   :  Scroll single-line field forward a line
2134 |
2135 |   Return Values :  E_OK                - success
2136 |                    E_REQUEST_DENIED    - no data ahead
2137 +--------------------------------------------------------------------------*/
2138 static int
2139 HSC_Horizontal_Line_Forward(FORM *form)
2140 {
2141   T((T_CALLED("HSC_Horizontal_Line_Forward(%p)"), form));
2142   returnCode(HSC_Generic(form, form->current->cols));
2143 }
2144 
2145 /*---------------------------------------------------------------------------
2146 |   Facility      :  libnform
2147 |   Function      :  static int HSC_Horizontal_Half_Line_Forward(FORM* form)
2148 |
2149 |   Description   :  Scroll single-line field forward half a line
2150 |
2151 |   Return Values :  E_OK               - success
2152 |                    E_REQUEST_DENIED   - no data ahead
2153 +--------------------------------------------------------------------------*/
2154 static int
2155 HSC_Horizontal_Half_Line_Forward(FORM *form)
2156 {
2157   T((T_CALLED("HSC_Horizontal_Half_Line_Forward(%p)"), form));
2158   returnCode(HSC_Generic(form, (form->current->cols + 1) / 2));
2159 }
2160 
2161 /*---------------------------------------------------------------------------
2162 |   Facility      :  libnform
2163 |   Function      :  static int HSC_Horizontal_Line_Backward(FORM* form)
2164 |
2165 |   Description   :  Scroll single-line field backward a line
2166 |
2167 |   Return Values :  E_OK                - success
2168 |                    E_REQUEST_DENIED    - no data behind
2169 +--------------------------------------------------------------------------*/
2170 static int
2171 HSC_Horizontal_Line_Backward(FORM *form)
2172 {
2173   T((T_CALLED("HSC_Horizontal_Line_Backward(%p)"), form));
2174   returnCode(HSC_Generic(form, -(form->current->cols)));
2175 }
2176 
2177 /*---------------------------------------------------------------------------
2178 |   Facility      :  libnform
2179 |   Function      :  static int HSC_Horizontal_Half_Line_Backward(FORM* form)
2180 |
2181 |   Description   :  Scroll single-line field backward half a line
2182 |
2183 |   Return Values :  E_OK                - success
2184 |                    E_REQUEST_DENIED    - no data behind
2185 +--------------------------------------------------------------------------*/
2186 static int
2187 HSC_Horizontal_Half_Line_Backward(FORM *form)
2188 {
2189   T((T_CALLED("HSC_Horizontal_Half_Line_Backward(%p)"), form));
2190   returnCode(HSC_Generic(form, -((form->current->cols + 1) / 2)));
2191 }
2192 
2193 /*----------------------------------------------------------------------------
2194   End of Horizontal scrolling routines
2195   --------------------------------------------------------------------------*/
2196 
2197 /*----------------------------------------------------------------------------
2198   Helper routines for Field Editing
2199   --------------------------------------------------------------------------*/
2200 
2201 /*---------------------------------------------------------------------------
2202 |   Facility      :  libnform
2203 |   Function      :  static bool Is_There_Room_For_A_Line(FORM * form)
2204 |
2205 |   Description   :  Check whether or not there is enough room in the
2206 |                    buffer to enter a whole line.
2207 |
2208 |   Return Values :  TRUE   - there is enough space
2209 |                    FALSE  - there is not enough space
2210 +--------------------------------------------------------------------------*/
2211 NCURSES_INLINE static bool
2212 Is_There_Room_For_A_Line(FORM *form)
2213 {
2214   FIELD *field = form->current;
2215   FIELD_CELL *begin_of_last_line, *s;
2216 
2217   Synchronize_Buffer(form);
2218   begin_of_last_line = Address_Of_Row_In_Buffer(field, (field->drows - 1));
2219   s = After_End_Of_Data(begin_of_last_line, field->dcols);
2220   return ((s == begin_of_last_line) ? TRUE : FALSE);
2221 }
2222 
2223 /*---------------------------------------------------------------------------
2224 |   Facility      :  libnform
2225 |   Function      :  static bool Is_There_Room_For_A_Char_In_Line(FORM * form)
2226 |
2227 |   Description   :  Checks whether or not there is room for a new character
2228 |                    in the current line.
2229 |
2230 |   Return Values :  TRUE    - there is room
2231 |                    FALSE   - there is not enough room (line full)
2232 +--------------------------------------------------------------------------*/
2233 NCURSES_INLINE static bool
2234 Is_There_Room_For_A_Char_In_Line(FORM *form)
2235 {
2236   int last_char_in_line;
2237 
2238   wmove(form->w, form->currow, form->current->dcols - 1);
2239   last_char_in_line = (int)(winch(form->w) & A_CHARTEXT);
2240   wmove(form->w, form->currow, form->curcol);
2241   return (((last_char_in_line == form->current->pad) ||
2242 	   is_blank(last_char_in_line)) ? TRUE : FALSE);
2243 }
2244 
2245 #define There_Is_No_Room_For_A_Char_In_Line(f) \
2246   !Is_There_Room_For_A_Char_In_Line(f)
2247 
2248 /*---------------------------------------------------------------------------
2249 |   Facility      :  libnform
2250 |   Function      :  static int Insert_String(
2251 |                                             FORM * form,
2252 |                                             int row,
2253 |                                             char *txt,
2254 |                                             int  len )
2255 |
2256 |   Description   :  Insert the 'len' characters beginning at pointer 'txt'
2257 |                    into the 'row' of the 'form'. The insertion occurs
2258 |                    on the beginning of the row, all other characters are
2259 |                    moved to the right. After the text a pad character will
2260 |                    be inserted to separate the text from the rest. If
2261 |                    necessary the insertion moves characters on the next
2262 |                    line to make place for the requested insertion string.
2263 |
2264 |   Return Values :  E_OK              - success
2265 |                    E_REQUEST_DENIED  -
2266 |                    E_SYSTEM_ERROR    - system error
2267 +--------------------------------------------------------------------------*/
2268 static int
2269 Insert_String(FORM *form, int row, FIELD_CELL *txt, int len)
2270 {
2271   FIELD *field = form->current;
2272   FIELD_CELL *bp = Address_Of_Row_In_Buffer(field, row);
2273   int datalen = (int)(After_End_Of_Data(bp, field->dcols) - bp);
2274   int freelen = field->dcols - datalen;
2275   int requiredlen = len + 1;
2276   FIELD_CELL *split;
2277   int result = E_REQUEST_DENIED;
2278 
2279   if (freelen >= requiredlen)
2280     {
2281       wmove(form->w, row, 0);
2282       myINSNSTR(form->w, txt, len);
2283       wmove(form->w, row, len);
2284       myINSNSTR(form->w, &myBLANK, 1);
2285       return E_OK;
2286     }
2287   else
2288     {
2289       /* we have to move characters on the next line. If we are on the
2290          last line this may work, if the field is growable */
2291       if ((row == (field->drows - 1)) && Growable(field))
2292 	{
2293 	  if (!Field_Grown(field, 1))
2294 	    return (E_SYSTEM_ERROR);
2295 	  /* !!!Side-Effect : might be changed due to growth!!! */
2296 	  bp = Address_Of_Row_In_Buffer(field, row);
2297 	}
2298 
2299       if (row < (field->drows - 1))
2300 	{
2301 	  split =
2302 	    After_Last_Whitespace_Character(bp,
2303 					    (int)(Get_Start_Of_Data(bp
2304 								    + field->dcols
2305 								    - requiredlen,
2306 								    requiredlen)
2307 						  - bp));
2308 	  /* split points now to the first character of the portion of the
2309 	     line that must be moved to the next line */
2310 	  datalen = (int)(split - bp);	/* + freelen has to stay on this line   */
2311 	  freelen = field->dcols - (datalen + freelen);		/* for the next line */
2312 
2313 	  if ((result = Insert_String(form, row + 1, split, freelen)) == E_OK)
2314 	    {
2315 	      wmove(form->w, row, datalen);
2316 	      wclrtoeol(form->w);
2317 	      wmove(form->w, row, 0);
2318 	      myINSNSTR(form->w, txt, len);
2319 	      wmove(form->w, row, len);
2320 	      myINSNSTR(form->w, &myBLANK, 1);
2321 	      return E_OK;
2322 	    }
2323 	}
2324       return (result);
2325     }
2326 }
2327 
2328 /*---------------------------------------------------------------------------
2329 |   Facility      :  libnform
2330 |   Function      :  static int Wrapping_Not_Necessary_Or_Wrapping_Ok(
2331 |                                             FORM * form)
2332 |
2333 |   Description   :  If a character has been entered into a field, it may
2334 |                    be that wrapping has to occur. This routine checks
2335 |                    whether or not wrapping is required and if so, performs
2336 |                    the wrapping.
2337 |
2338 |   Return Values :  E_OK              - no wrapping required or wrapping
2339 |                                        was successful
2340 |                    E_REQUEST_DENIED  -
2341 |                    E_SYSTEM_ERROR    - some system error
2342 +--------------------------------------------------------------------------*/
2343 static int
2344 Wrapping_Not_Necessary_Or_Wrapping_Ok(FORM *form)
2345 {
2346   FIELD *field = form->current;
2347   int result = E_REQUEST_DENIED;
2348   bool Last_Row = ((field->drows - 1) == form->currow);
2349 
2350   if ((field->opts & O_WRAP) &&	/* wrapping wanted     */
2351       (!Single_Line_Field(field)) &&	/* must be multi-line  */
2352       (There_Is_No_Room_For_A_Char_In_Line(form)) &&	/* line is full        */
2353       (!Last_Row || Growable(field)))	/* there are more lines */
2354     {
2355       FIELD_CELL *bp;
2356       FIELD_CELL *split;
2357       int chars_to_be_wrapped;
2358       int chars_to_remain_on_line;
2359 
2360       if (Last_Row)
2361 	{
2362 	  /* the above logic already ensures, that in this case the field
2363 	     is growable */
2364 	  if (!Field_Grown(field, 1))
2365 	    return E_SYSTEM_ERROR;
2366 	}
2367       bp = Address_Of_Current_Row_In_Buffer(form);
2368       Window_To_Buffer(form->w, field);
2369       split = After_Last_Whitespace_Character(bp, field->dcols);
2370       /* split points to the first character of the sequence to be brought
2371          on the next line */
2372       chars_to_remain_on_line = (int)(split - bp);
2373       chars_to_be_wrapped = field->dcols - chars_to_remain_on_line;
2374       if (chars_to_remain_on_line > 0)
2375 	{
2376 	  if ((result = Insert_String(form, form->currow + 1, split,
2377 				      chars_to_be_wrapped)) == E_OK)
2378 	    {
2379 	      wmove(form->w, form->currow, chars_to_remain_on_line);
2380 	      wclrtoeol(form->w);
2381 	      if (form->curcol >= chars_to_remain_on_line)
2382 		{
2383 		  form->currow++;
2384 		  form->curcol -= chars_to_remain_on_line;
2385 		}
2386 	      return E_OK;
2387 	    }
2388 	}
2389       else
2390 	return E_OK;
2391       if (result != E_OK)
2392 	{
2393 	  DeleteChar(form);
2394 	  Window_To_Buffer(form->w, field);
2395 	  result = E_REQUEST_DENIED;
2396 	}
2397     }
2398   else
2399     result = E_OK;		/* wrapping was not necessary */
2400   return (result);
2401 }
2402 
2403 /*----------------------------------------------------------------------------
2404   Field Editing routines
2405   --------------------------------------------------------------------------*/
2406 
2407 /*---------------------------------------------------------------------------
2408 |   Facility      :  libnform
2409 |   Function      :  static int Field_Editing(
2410 |                                    int (* const fct) (FORM *),
2411 |                                    FORM * form)
2412 |
2413 |   Description   :  Generic routine for field editing requests. The driver
2414 |                    routines are only called for editable fields, the
2415 |                    _WINDOW_MODIFIED flag is set if editing occurred.
2416 |                    This is somewhat special due to the overload semantics
2417 |                    of the NEW_LINE and DEL_PREV requests.
2418 |
2419 |   Return Values :  Error code from low level drivers.
2420 +--------------------------------------------------------------------------*/
2421 static int
2422 Field_Editing(int (*const fct) (FORM *), FORM *form)
2423 {
2424   int res = E_REQUEST_DENIED;
2425 
2426   /* We have to deal here with the specific case of the overloaded
2427      behavior of New_Line and Delete_Previous requests.
2428      They may end up in navigational requests if we are on the first
2429      character in a field. But navigation is also allowed on non-
2430      editable fields.
2431    */
2432   if ((fct == FE_Delete_Previous) &&
2433       (form->opts & O_BS_OVERLOAD) &&
2434       First_Position_In_Current_Field(form))
2435     {
2436       res = Inter_Field_Navigation(FN_Previous_Field, form);
2437     }
2438   else
2439     {
2440       if (fct == FE_New_Line)
2441 	{
2442 	  if ((form->opts & O_NL_OVERLOAD) &&
2443 	      First_Position_In_Current_Field(form))
2444 	    {
2445 	      res = Inter_Field_Navigation(FN_Next_Field, form);
2446 	    }
2447 	  else
2448 	    /* FE_New_Line deals itself with the _WINDOW_MODIFIED flag */
2449 	    res = fct(form);
2450 	}
2451       else
2452 	{
2453 	  /* From now on, everything must be editable */
2454 	  if (form->current->opts & O_EDIT)
2455 	    {
2456 	      res = fct(form);
2457 	      if (res == E_OK)
2458 		form->status |= _WINDOW_MODIFIED;
2459 	    }
2460 	}
2461     }
2462   return res;
2463 }
2464 
2465 /*---------------------------------------------------------------------------
2466 |   Facility      :  libnform
2467 |   Function      :  static int FE_New_Line(FORM * form)
2468 |
2469 |   Description   :  Perform a new line request. This is rather complex
2470 |                    compared to other routines in this code due to the
2471 |                    rather difficult to understand description in the
2472 |                    manuals.
2473 |
2474 |   Return Values :  E_OK               - success
2475 |                    E_REQUEST_DENIED   - new line not allowed
2476 |                    E_SYSTEM_ERROR     - system error
2477 +--------------------------------------------------------------------------*/
2478 static int
2479 FE_New_Line(FORM *form)
2480 {
2481   FIELD *field = form->current;
2482   FIELD_CELL *bp, *t;
2483   bool Last_Row = ((field->drows - 1) == form->currow);
2484 
2485   T((T_CALLED("FE_New_Line(%p)"), form));
2486   if (form->status & _OVLMODE)
2487     {
2488       if (Last_Row &&
2489 	  (!(Growable(field) && !Single_Line_Field(field))))
2490 	{
2491 	  if (!(form->opts & O_NL_OVERLOAD))
2492 	    returnCode(E_REQUEST_DENIED);
2493 	  wmove(form->w, form->currow, form->curcol);
2494 	  wclrtoeol(form->w);
2495 	  /* we have to set this here, although it is also
2496 	     handled in the generic routine. The reason is,
2497 	     that FN_Next_Field may fail, but the form is
2498 	     definitively changed */
2499 	  form->status |= _WINDOW_MODIFIED;
2500 	  returnCode(Inter_Field_Navigation(FN_Next_Field, form));
2501 	}
2502       else
2503 	{
2504 	  if (Last_Row && !Field_Grown(field, 1))
2505 	    {
2506 	      /* N.B.: due to the logic in the 'if', LastRow==TRUE
2507 	         means here that the field is growable and not
2508 	         a single-line field */
2509 	      returnCode(E_SYSTEM_ERROR);
2510 	    }
2511 	  wmove(form->w, form->currow, form->curcol);
2512 	  wclrtoeol(form->w);
2513 	  form->currow++;
2514 	  form->curcol = 0;
2515 	  form->status |= _WINDOW_MODIFIED;
2516 	  returnCode(E_OK);
2517 	}
2518     }
2519   else
2520     {
2521       /* Insert Mode */
2522       if (Last_Row &&
2523 	  !(Growable(field) && !Single_Line_Field(field)))
2524 	{
2525 	  if (!(form->opts & O_NL_OVERLOAD))
2526 	    returnCode(E_REQUEST_DENIED);
2527 	  returnCode(Inter_Field_Navigation(FN_Next_Field, form));
2528 	}
2529       else
2530 	{
2531 	  bool May_Do_It = !Last_Row && Is_There_Room_For_A_Line(form);
2532 
2533 	  if (!(May_Do_It || Growable(field)))
2534 	    returnCode(E_REQUEST_DENIED);
2535 	  if (!May_Do_It && !Field_Grown(field, 1))
2536 	    returnCode(E_SYSTEM_ERROR);
2537 
2538 	  bp = Address_Of_Current_Position_In_Buffer(form);
2539 	  t = After_End_Of_Data(bp, field->dcols - form->curcol);
2540 	  wmove(form->w, form->currow, form->curcol);
2541 	  wclrtoeol(form->w);
2542 	  form->currow++;
2543 	  form->curcol = 0;
2544 	  wmove(form->w, form->currow, form->curcol);
2545 	  winsertln(form->w);
2546 	  myADDNSTR(form->w, bp, (int)(t - bp));
2547 	  form->status |= _WINDOW_MODIFIED;
2548 	  returnCode(E_OK);
2549 	}
2550     }
2551 }
2552 
2553 /*---------------------------------------------------------------------------
2554 |   Facility      :  libnform
2555 |   Function      :  static int FE_Insert_Character(FORM * form)
2556 |
2557 |   Description   :  Insert blank character at the cursor position
2558 |
2559 |   Return Values :  E_OK
2560 |                    E_REQUEST_DENIED
2561 +--------------------------------------------------------------------------*/
2562 static int
2563 FE_Insert_Character(FORM *form)
2564 {
2565   FIELD *field = form->current;
2566   int result = E_REQUEST_DENIED;
2567 
2568   T((T_CALLED("FE_Insert_Character(%p)"), form));
2569   if (Check_Char(field->type, (int)C_BLANK, (TypeArgument *)(field->arg)))
2570     {
2571       bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
2572 
2573       if (There_Is_Room ||
2574 	  ((Single_Line_Field(field) && Growable(field))))
2575 	{
2576 	  if (!There_Is_Room && !Field_Grown(field, 1))
2577 	    result = E_SYSTEM_ERROR;
2578 	  else
2579 	    {
2580 	      winsch(form->w, (chtype)C_BLANK);
2581 	      result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form);
2582 	    }
2583 	}
2584     }
2585   returnCode(result);
2586 }
2587 
2588 /*---------------------------------------------------------------------------
2589 |   Facility      :  libnform
2590 |   Function      :  static int FE_Insert_Line(FORM * form)
2591 |
2592 |   Description   :  Insert a blank line at the cursor position
2593 |
2594 |   Return Values :  E_OK               - success
2595 |                    E_REQUEST_DENIED   - line can not be inserted
2596 +--------------------------------------------------------------------------*/
2597 static int
2598 FE_Insert_Line(FORM *form)
2599 {
2600   FIELD *field = form->current;
2601   int result = E_REQUEST_DENIED;
2602 
2603   T((T_CALLED("FE_Insert_Line(%p)"), form));
2604   if (Check_Char(field->type, (int)C_BLANK, (TypeArgument *)(field->arg)))
2605     {
2606       bool Maybe_Done = (form->currow != (field->drows - 1)) &&
2607       Is_There_Room_For_A_Line(form);
2608 
2609       if (!Single_Line_Field(field) &&
2610 	  (Maybe_Done || Growable(field)))
2611 	{
2612 	  if (!Maybe_Done && !Field_Grown(field, 1))
2613 	    result = E_SYSTEM_ERROR;
2614 	  else
2615 	    {
2616 	      form->curcol = 0;
2617 	      winsertln(form->w);
2618 	      result = E_OK;
2619 	    }
2620 	}
2621     }
2622   returnCode(result);
2623 }
2624 
2625 /*---------------------------------------------------------------------------
2626 |   Facility      :  libnform
2627 |   Function      :  static int FE_Delete_Character(FORM * form)
2628 |
2629 |   Description   :  Delete character at the cursor position
2630 |
2631 |   Return Values :  E_OK    - success
2632 +--------------------------------------------------------------------------*/
2633 static int
2634 FE_Delete_Character(FORM *form)
2635 {
2636   T((T_CALLED("FE_Delete_Character(%p)"), form));
2637   DeleteChar(form);
2638   returnCode(E_OK);
2639 }
2640 
2641 /*---------------------------------------------------------------------------
2642 |   Facility      :  libnform
2643 |   Function      :  static int FE_Delete_Previous(FORM * form)
2644 |
2645 |   Description   :  Delete character before cursor. Again this is a rather
2646 |                    difficult piece compared to others due to the overloading
2647 |                    semantics of backspace.
2648 |                    N.B.: The case of overloaded BS on first field position
2649 |                          is already handled in the generic routine.
2650 |
2651 |   Return Values :  E_OK                - success
2652 |                    E_REQUEST_DENIED    - Character can't be deleted
2653 +--------------------------------------------------------------------------*/
2654 static int
2655 FE_Delete_Previous(FORM *form)
2656 {
2657   FIELD *field = form->current;
2658 
2659   T((T_CALLED("FE_Delete_Previous(%p)"), form));
2660   if (First_Position_In_Current_Field(form))
2661     returnCode(E_REQUEST_DENIED);
2662 
2663   if ((--(form->curcol)) < 0)
2664     {
2665       FIELD_CELL *this_line, *prev_line, *prev_end, *this_end;
2666       int this_row = form->currow;
2667 
2668       form->curcol++;
2669       if (form->status & _OVLMODE)
2670 	returnCode(E_REQUEST_DENIED);
2671 
2672       prev_line = Address_Of_Row_In_Buffer(field, (form->currow - 1));
2673       this_line = Address_Of_Row_In_Buffer(field, (form->currow));
2674       Synchronize_Buffer(form);
2675       prev_end = After_End_Of_Data(prev_line, field->dcols);
2676       this_end = After_End_Of_Data(this_line, field->dcols);
2677       if ((int)(this_end - this_line) >
2678 	  (field->cols - (int)(prev_end - prev_line)))
2679 	returnCode(E_REQUEST_DENIED);
2680       wmove(form->w, form->currow, form->curcol);
2681       wdeleteln(form->w);
2682       Adjust_Cursor_Position(form, prev_end);
2683       /*
2684        * If we did not really move to the previous line, help the user a
2685        * little.  It is however a little inconsistent.  Normally, when
2686        * backspacing around the point where text wraps to a new line in a
2687        * multi-line form, we absorb one keystroke for the wrapping point.  That
2688        * is consistent with SVr4 forms.  However, SVr4 does not allow typing
2689        * into the last column of the field, and requires the user to enter a
2690        * newline to move to the next line.  Therefore it can consistently eat
2691        * that keystroke.  Since ncurses allows the last column, it wraps
2692        * automatically (given the proper options).  But we cannot eat the
2693        * keystroke to back over the wrapping point, since that would put the
2694        * cursor past the end of the form field.  In this case, just delete the
2695        * character at the end of the field.
2696        */
2697       if (form->currow == this_row && this_row > 0)
2698 	{
2699 	  form->currow -= 1;
2700 	  form->curcol = field->dcols - 1;
2701 	  DeleteChar(form);
2702 	}
2703       else
2704 	{
2705 	  wmove(form->w, form->currow, form->curcol);
2706 	  myADDNSTR(form->w, this_line, (int)(this_end - this_line));
2707 	}
2708     }
2709   else
2710     {
2711       DeleteChar(form);
2712     }
2713   returnCode(E_OK);
2714 }
2715 
2716 /*---------------------------------------------------------------------------
2717 |   Facility      :  libnform
2718 |   Function      :  static int FE_Delete_Line(FORM * form)
2719 |
2720 |   Description   :  Delete line at cursor position.
2721 |
2722 |   Return Values :  E_OK  - success
2723 +--------------------------------------------------------------------------*/
2724 static int
2725 FE_Delete_Line(FORM *form)
2726 {
2727   T((T_CALLED("FE_Delete_Line(%p)"), form));
2728   form->curcol = 0;
2729   wdeleteln(form->w);
2730   returnCode(E_OK);
2731 }
2732 
2733 /*---------------------------------------------------------------------------
2734 |   Facility      :  libnform
2735 |   Function      :  static int FE_Delete_Word(FORM * form)
2736 |
2737 |   Description   :  Delete word at cursor position
2738 |
2739 |   Return Values :  E_OK               - success
2740 |                    E_REQUEST_DENIED   - failure
2741 +--------------------------------------------------------------------------*/
2742 static int
2743 FE_Delete_Word(FORM *form)
2744 {
2745   FIELD *field = form->current;
2746   FIELD_CELL *bp = Address_Of_Current_Row_In_Buffer(form);
2747   FIELD_CELL *ep = bp + field->dcols;
2748   FIELD_CELL *cp = bp + form->curcol;
2749   FIELD_CELL *s;
2750 
2751   T((T_CALLED("FE_Delete_Word(%p)"), form));
2752   Synchronize_Buffer(form);
2753   if (ISBLANK(*cp))
2754     returnCode(E_REQUEST_DENIED);	/* not in word */
2755 
2756   /* move cursor to begin of word and erase to end of screen-line */
2757   Adjust_Cursor_Position(form,
2758 			 After_Last_Whitespace_Character(bp, form->curcol));
2759   wmove(form->w, form->currow, form->curcol);
2760   wclrtoeol(form->w);
2761 
2762   /* skip over word in buffer */
2763   s = Get_First_Whitespace_Character(cp, (int)(ep - cp));
2764   /* to begin of next word    */
2765   s = Get_Start_Of_Data(s, (int)(ep - s));
2766   if ((s != cp) && !ISBLANK(*s))
2767     {
2768       /* copy remaining line to window */
2769       myADDNSTR(form->w, s, (int)(s - After_End_Of_Data(s, (int)(ep - s))));
2770     }
2771   returnCode(E_OK);
2772 }
2773 
2774 /*---------------------------------------------------------------------------
2775 |   Facility      :  libnform
2776 |   Function      :  static int FE_Clear_To_End_Of_Line(FORM * form)
2777 |
2778 |   Description   :  Clear to end of current line.
2779 |
2780 |   Return Values :  E_OK   - success
2781 +--------------------------------------------------------------------------*/
2782 static int
2783 FE_Clear_To_End_Of_Line(FORM *form)
2784 {
2785   T((T_CALLED("FE_Clear_To_End_Of_Line(%p)"), form));
2786   wmove(form->w, form->currow, form->curcol);
2787   wclrtoeol(form->w);
2788   returnCode(E_OK);
2789 }
2790 
2791 /*---------------------------------------------------------------------------
2792 |   Facility      :  libnform
2793 |   Function      :  static int FE_Clear_To_End_Of_Field(FORM * form)
2794 |
2795 |   Description   :  Clear to end of field.
2796 |
2797 |   Return Values :  E_OK   - success
2798 +--------------------------------------------------------------------------*/
2799 static int
2800 FE_Clear_To_End_Of_Field(FORM *form)
2801 {
2802   T((T_CALLED("FE_Clear_To_End_Of_Field(%p)"), form));
2803   wmove(form->w, form->currow, form->curcol);
2804   wclrtobot(form->w);
2805   returnCode(E_OK);
2806 }
2807 
2808 /*---------------------------------------------------------------------------
2809 |   Facility      :  libnform
2810 |   Function      :  static int FE_Clear_Field(FORM * form)
2811 |
2812 |   Description   :  Clear entire field.
2813 |
2814 |   Return Values :  E_OK   - success
2815 +--------------------------------------------------------------------------*/
2816 static int
2817 FE_Clear_Field(FORM *form)
2818 {
2819   T((T_CALLED("FE_Clear_Field(%p)"), form));
2820   form->currow = form->curcol = 0;
2821   werase(form->w);
2822   returnCode(E_OK);
2823 }
2824 /*----------------------------------------------------------------------------
2825   END of Field Editing routines
2826   --------------------------------------------------------------------------*/
2827 
2828 /*----------------------------------------------------------------------------
2829   Edit Mode routines
2830   --------------------------------------------------------------------------*/
2831 
2832 /*---------------------------------------------------------------------------
2833 |   Facility      :  libnform
2834 |   Function      :  static int EM_Overlay_Mode(FORM * form)
2835 |
2836 |   Description   :  Switch to overlay mode.
2837 |
2838 |   Return Values :  E_OK   - success
2839 +--------------------------------------------------------------------------*/
2840 static int
2841 EM_Overlay_Mode(FORM *form)
2842 {
2843   T((T_CALLED("EM_Overlay_Mode(%p)"), form));
2844   form->status |= _OVLMODE;
2845   returnCode(E_OK);
2846 }
2847 
2848 /*---------------------------------------------------------------------------
2849 |   Facility      :  libnform
2850 |   Function      :  static int EM_Insert_Mode(FORM * form)
2851 |
2852 |   Description   :  Switch to insert mode
2853 |
2854 |   Return Values :  E_OK   - success
2855 +--------------------------------------------------------------------------*/
2856 static int
2857 EM_Insert_Mode(FORM *form)
2858 {
2859   T((T_CALLED("EM_Insert_Mode(%p)"), form));
2860   form->status &= ~_OVLMODE;
2861   returnCode(E_OK);
2862 }
2863 
2864 /*----------------------------------------------------------------------------
2865   END of Edit Mode routines
2866   --------------------------------------------------------------------------*/
2867 
2868 /*----------------------------------------------------------------------------
2869   Helper routines for Choice Requests
2870   --------------------------------------------------------------------------*/
2871 
2872 /*---------------------------------------------------------------------------
2873 |   Facility      :  libnform
2874 |   Function      :  static bool Next_Choice(
2875 |                                            FIELDTYPE * typ,
2876 |                                            FIELD * field,
2877 |                                            TypeArgument *argp)
2878 |
2879 |   Description   :  Get the next field choice. For linked types this is
2880 |                    done recursively.
2881 |
2882 |   Return Values :  TRUE    - next choice successfully retrieved
2883 |                    FALSE   - couldn't retrieve next choice
2884 +--------------------------------------------------------------------------*/
2885 static bool
2886 Next_Choice(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
2887 {
2888   if (!typ || !(typ->status & _HAS_CHOICE))
2889     return FALSE;
2890 
2891   if (typ->status & _LINKED_TYPE)
2892     {
2893       assert(argp);
2894       return (
2895 	       Next_Choice(typ->left, field, argp->left) ||
2896 	       Next_Choice(typ->right, field, argp->right));
2897     }
2898   else
2899     {
2900       assert(typ->next);
2901       return typ->next(field, (void *)argp);
2902     }
2903 }
2904 
2905 /*---------------------------------------------------------------------------
2906 |   Facility      :  libnform
2907 |   Function      :  static bool Previous_Choice(
2908 |                                                FIELDTYPE * typ,
2909 |                                                FIELD * field,
2910 |                                                TypeArgument *argp)
2911 |
2912 |   Description   :  Get the previous field choice. For linked types this
2913 |                    is done recursively.
2914 |
2915 |   Return Values :  TRUE    - previous choice successfully retrieved
2916 |                    FALSE   - couldn't retrieve previous choice
2917 +--------------------------------------------------------------------------*/
2918 static bool
2919 Previous_Choice(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
2920 {
2921   if (!typ || !(typ->status & _HAS_CHOICE))
2922     return FALSE;
2923 
2924   if (typ->status & _LINKED_TYPE)
2925     {
2926       assert(argp);
2927       return (
2928 	       Previous_Choice(typ->left, field, argp->left) ||
2929 	       Previous_Choice(typ->right, field, argp->right));
2930     }
2931   else
2932     {
2933       assert(typ->prev);
2934       return typ->prev(field, (void *)argp);
2935     }
2936 }
2937 /*----------------------------------------------------------------------------
2938   End of Helper routines for Choice Requests
2939   --------------------------------------------------------------------------*/
2940 
2941 /*----------------------------------------------------------------------------
2942   Routines for Choice Requests
2943   --------------------------------------------------------------------------*/
2944 
2945 /*---------------------------------------------------------------------------
2946 |   Facility      :  libnform
2947 |   Function      :  static int CR_Next_Choice(FORM * form)
2948 |
2949 |   Description   :  Get the next field choice.
2950 |
2951 |   Return Values :  E_OK              - success
2952 |                    E_REQUEST_DENIED  - next choice couldn't be retrieved
2953 +--------------------------------------------------------------------------*/
2954 static int
2955 CR_Next_Choice(FORM *form)
2956 {
2957   FIELD *field = form->current;
2958 
2959   T((T_CALLED("CR_Next_Choice(%p)"), form));
2960   Synchronize_Buffer(form);
2961   returnCode((Next_Choice(field->type, field, (TypeArgument *)(field->arg)))
2962 	     ? E_OK
2963 	     : E_REQUEST_DENIED);
2964 }
2965 
2966 /*---------------------------------------------------------------------------
2967 |   Facility      :  libnform
2968 |   Function      :  static int CR_Previous_Choice(FORM * form)
2969 |
2970 |   Description   :  Get the previous field choice.
2971 |
2972 |   Return Values :  E_OK              - success
2973 |                    E_REQUEST_DENIED  - prev. choice couldn't be retrieved
2974 +--------------------------------------------------------------------------*/
2975 static int
2976 CR_Previous_Choice(FORM *form)
2977 {
2978   FIELD *field = form->current;
2979 
2980   T((T_CALLED("CR_Previous_Choice(%p)"), form));
2981   Synchronize_Buffer(form);
2982   returnCode((Previous_Choice(field->type, field, (TypeArgument *)(field->arg)))
2983 	     ? E_OK
2984 	     : E_REQUEST_DENIED);
2985 }
2986 /*----------------------------------------------------------------------------
2987   End of Routines for Choice Requests
2988   --------------------------------------------------------------------------*/
2989 
2990 /*----------------------------------------------------------------------------
2991   Helper routines for Field Validations.
2992   --------------------------------------------------------------------------*/
2993 
2994 /*---------------------------------------------------------------------------
2995 |   Facility      :  libnform
2996 |   Function      :  static bool Check_Field(
2997 |                                            FIELDTYPE * typ,
2998 |                                            FIELD * field,
2999 |                                            TypeArgument * argp)
3000 |
3001 |   Description   :  Check the field according to its fieldtype and its
3002 |                    actual arguments. For linked fieldtypes this is done
3003 |                    recursively.
3004 |
3005 |   Return Values :  TRUE       - field is valid
3006 |                    FALSE      - field is invalid.
3007 +--------------------------------------------------------------------------*/
3008 static bool
3009 Check_Field(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
3010 {
3011   if (typ)
3012     {
3013       if (field->opts & O_NULLOK)
3014 	{
3015 	  FIELD_CELL *bp = field->buf;
3016 
3017 	  assert(bp);
3018 	  while (ISBLANK(*bp))
3019 	    {
3020 	      bp++;
3021 	    }
3022 	  if (CharOf(*bp) == 0)
3023 	    return TRUE;
3024 	}
3025 
3026       if (typ->status & _LINKED_TYPE)
3027 	{
3028 	  assert(argp);
3029 	  return (
3030 		   Check_Field(typ->left, field, argp->left) ||
3031 		   Check_Field(typ->right, field, argp->right));
3032 	}
3033       else
3034 	{
3035 	  if (typ->fcheck)
3036 	    return typ->fcheck(field, (void *)argp);
3037 	}
3038     }
3039   return TRUE;
3040 }
3041 
3042 /*---------------------------------------------------------------------------
3043 |   Facility      :  libnform
3044 |   Function      :  bool _nc_Internal_Validation(FORM * form )
3045 |
3046 |   Description   :  Validate the current field of the form.
3047 |
3048 |   Return Values :  TRUE  - field is valid
3049 |                    FALSE - field is invalid
3050 +--------------------------------------------------------------------------*/
3051 NCURSES_EXPORT(bool)
3052 _nc_Internal_Validation(FORM *form)
3053 {
3054   FIELD *field;
3055 
3056   field = form->current;
3057 
3058   Synchronize_Buffer(form);
3059   if ((form->status & _FCHECK_REQUIRED) ||
3060       (!(field->opts & O_PASSOK)))
3061     {
3062       if (!Check_Field(field->type, field, (TypeArgument *)(field->arg)))
3063 	return FALSE;
3064       form->status &= ~_FCHECK_REQUIRED;
3065       field->status |= _CHANGED;
3066       Synchronize_Linked_Fields(field);
3067     }
3068   return TRUE;
3069 }
3070 /*----------------------------------------------------------------------------
3071   End of Helper routines for Field Validations.
3072   --------------------------------------------------------------------------*/
3073 
3074 /*----------------------------------------------------------------------------
3075   Routines for Field Validation.
3076   --------------------------------------------------------------------------*/
3077 
3078 /*---------------------------------------------------------------------------
3079 |   Facility      :  libnform
3080 |   Function      :  static int FV_Validation(FORM * form)
3081 |
3082 |   Description   :  Validate the current field of the form.
3083 |
3084 |   Return Values :  E_OK             - field valid
3085 |                    E_INVALID_FIELD  - field not valid
3086 +--------------------------------------------------------------------------*/
3087 static int
3088 FV_Validation(FORM *form)
3089 {
3090   T((T_CALLED("FV_Validation(%p)"), form));
3091   if (_nc_Internal_Validation(form))
3092     returnCode(E_OK);
3093   else
3094     returnCode(E_INVALID_FIELD);
3095 }
3096 /*----------------------------------------------------------------------------
3097   End of routines for Field Validation.
3098   --------------------------------------------------------------------------*/
3099 
3100 /*----------------------------------------------------------------------------
3101   Helper routines for Inter-Field Navigation
3102   --------------------------------------------------------------------------*/
3103 
3104 /*---------------------------------------------------------------------------
3105 |   Facility      :  libnform
3106 |   Function      :  static FIELD *Next_Field_On_Page(FIELD * field)
3107 |
3108 |   Description   :  Get the next field after the given field on the current
3109 |                    page. The order of fields is the one defined by the
3110 |                    fields array. Only visible and active fields are
3111 |                    counted.
3112 |
3113 |   Return Values :  Pointer to the next field.
3114 +--------------------------------------------------------------------------*/
3115 NCURSES_INLINE static FIELD *
3116 Next_Field_On_Page(FIELD *field)
3117 {
3118   FORM *form = field->form;
3119   FIELD **field_on_page = &form->field[field->index];
3120   FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
3121   FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3122 
3123   do
3124     {
3125       field_on_page =
3126 	(field_on_page == last_on_page) ? first_on_page : field_on_page + 1;
3127       if (Field_Is_Selectable(*field_on_page))
3128 	break;
3129     }
3130   while (field != (*field_on_page));
3131   return (*field_on_page);
3132 }
3133 
3134 /*---------------------------------------------------------------------------
3135 |   Facility      :  libnform
3136 |   Function      :  FIELD* _nc_First_Active_Field(FORM * form)
3137 |
3138 |   Description   :  Get the first active field on the current page,
3139 |                    if there are such. If there are none, get the first
3140 |                    visible field on the page. If there are also none,
3141 |                    we return the first field on page and hope the best.
3142 |
3143 |   Return Values :  Pointer to calculated field.
3144 +--------------------------------------------------------------------------*/
3145 NCURSES_EXPORT(FIELD *)
3146 _nc_First_Active_Field(FORM *form)
3147 {
3148   FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3149   FIELD *proposed = Next_Field_On_Page(*last_on_page);
3150 
3151   if (proposed == *last_on_page)
3152     {
3153       /* there might be the special situation, where there is no
3154          active and visible field on the current page. We then select
3155          the first visible field on this readonly page
3156        */
3157       if (Field_Is_Not_Selectable(proposed))
3158 	{
3159 	  FIELD **field = &form->field[proposed->index];
3160 	  FIELD **first = &form->field[form->page[form->curpage].pmin];
3161 
3162 	  do
3163 	    {
3164 	      field = (field == last_on_page) ? first : field + 1;
3165 	      if (((*field)->opts & O_VISIBLE))
3166 		break;
3167 	    }
3168 	  while (proposed != (*field));
3169 
3170 	  proposed = *field;
3171 
3172 	  if ((proposed == *last_on_page) && !(proposed->opts & O_VISIBLE))
3173 	    {
3174 	      /* This means, there is also no visible field on the page.
3175 	         So we propose the first one and hope the very best...
3176 	         Some very clever user has designed a readonly and invisible
3177 	         page on this form.
3178 	       */
3179 	      proposed = *first;
3180 	    }
3181 	}
3182     }
3183   return (proposed);
3184 }
3185 
3186 /*---------------------------------------------------------------------------
3187 |   Facility      :  libnform
3188 |   Function      :  static FIELD *Previous_Field_On_Page(FIELD * field)
3189 |
3190 |   Description   :  Get the previous field before the given field on the
3191 |                    current page. The order of fields is the one defined by
3192 |                    the fields array. Only visible and active fields are
3193 |                    counted.
3194 |
3195 |   Return Values :  Pointer to the previous field.
3196 +--------------------------------------------------------------------------*/
3197 NCURSES_INLINE static FIELD *
3198 Previous_Field_On_Page(FIELD *field)
3199 {
3200   FORM *form = field->form;
3201   FIELD **field_on_page = &form->field[field->index];
3202   FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
3203   FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3204 
3205   do
3206     {
3207       field_on_page =
3208 	(field_on_page == first_on_page) ? last_on_page : field_on_page - 1;
3209       if (Field_Is_Selectable(*field_on_page))
3210 	break;
3211     }
3212   while (field != (*field_on_page));
3213 
3214   return (*field_on_page);
3215 }
3216 
3217 /*---------------------------------------------------------------------------
3218 |   Facility      :  libnform
3219 |   Function      :  static FIELD *Sorted_Next_Field(FIELD * field)
3220 |
3221 |   Description   :  Get the next field after the given field on the current
3222 |                    page. The order of fields is the one defined by the
3223 |                    (row,column) geometry, rows are major.
3224 |
3225 |   Return Values :  Pointer to the next field.
3226 +--------------------------------------------------------------------------*/
3227 NCURSES_INLINE static FIELD *
3228 Sorted_Next_Field(FIELD *field)
3229 {
3230   FIELD *field_on_page = field;
3231 
3232   do
3233     {
3234       field_on_page = field_on_page->snext;
3235       if (Field_Is_Selectable(field_on_page))
3236 	break;
3237     }
3238   while (field_on_page != field);
3239 
3240   return (field_on_page);
3241 }
3242 
3243 /*---------------------------------------------------------------------------
3244 |   Facility      :  libnform
3245 |   Function      :  static FIELD *Sorted_Previous_Field(FIELD * field)
3246 |
3247 |   Description   :  Get the previous field before the given field on the
3248 |                    current page. The order of fields is the one defined
3249 |                    by the (row,column) geometry, rows are major.
3250 |
3251 |   Return Values :  Pointer to the previous field.
3252 +--------------------------------------------------------------------------*/
3253 NCURSES_INLINE static FIELD *
3254 Sorted_Previous_Field(FIELD *field)
3255 {
3256   FIELD *field_on_page = field;
3257 
3258   do
3259     {
3260       field_on_page = field_on_page->sprev;
3261       if (Field_Is_Selectable(field_on_page))
3262 	break;
3263     }
3264   while (field_on_page != field);
3265 
3266   return (field_on_page);
3267 }
3268 
3269 /*---------------------------------------------------------------------------
3270 |   Facility      :  libnform
3271 |   Function      :  static FIELD *Left_Neighbor_Field(FIELD * field)
3272 |
3273 |   Description   :  Get the left neighbor of the field on the same line
3274 |                    and the same page. Cycles through the line.
3275 |
3276 |   Return Values :  Pointer to left neighbor field.
3277 +--------------------------------------------------------------------------*/
3278 NCURSES_INLINE static FIELD *
3279 Left_Neighbor_Field(FIELD *field)
3280 {
3281   FIELD *field_on_page = field;
3282 
3283   /* For a field that has really a left neighbor, the while clause
3284      immediately fails and the loop is left, positioned at the right
3285      neighbor. Otherwise we cycle backwards through the sorted field list
3286      until we enter the same line (from the right end).
3287    */
3288   do
3289     {
3290       field_on_page = Sorted_Previous_Field(field_on_page);
3291     }
3292   while (field_on_page->frow != field->frow);
3293 
3294   return (field_on_page);
3295 }
3296 
3297 /*---------------------------------------------------------------------------
3298 |   Facility      :  libnform
3299 |   Function      :  static FIELD *Right_Neighbor_Field(FIELD * field)
3300 |
3301 |   Description   :  Get the right neighbor of the field on the same line
3302 |                    and the same page.
3303 |
3304 |   Return Values :  Pointer to right neighbor field.
3305 +--------------------------------------------------------------------------*/
3306 NCURSES_INLINE static FIELD *
3307 Right_Neighbor_Field(FIELD *field)
3308 {
3309   FIELD *field_on_page = field;
3310 
3311   /* See the comments on Left_Neighbor_Field to understand how it works */
3312   do
3313     {
3314       field_on_page = Sorted_Next_Field(field_on_page);
3315     }
3316   while (field_on_page->frow != field->frow);
3317 
3318   return (field_on_page);
3319 }
3320 
3321 /*---------------------------------------------------------------------------
3322 |   Facility      :  libnform
3323 |   Function      :  static FIELD *Upper_Neighbor_Field(FIELD * field)
3324 |
3325 |   Description   :  Because of the row-major nature of sorting the fields,
3326 |                    it is more difficult to define whats the upper neighbor
3327 |                    field really means. We define that it must be on a
3328 |                    'previous' line (cyclic order!) and is the rightmost
3329 |                    field laying on the left side of the given field. If
3330 |                    this set is empty, we take the first field on the line.
3331 |
3332 |   Return Values :  Pointer to the upper neighbor field.
3333 +--------------------------------------------------------------------------*/
3334 static FIELD *
3335 Upper_Neighbor_Field(FIELD *field)
3336 {
3337   FIELD *field_on_page = field;
3338   int frow = field->frow;
3339   int fcol = field->fcol;
3340 
3341   /* Walk back to the 'previous' line. The second term in the while clause
3342      just guarantees that we stop if we cycled through the line because
3343      there might be no 'previous' line if the page has just one line.
3344    */
3345   do
3346     {
3347       field_on_page = Sorted_Previous_Field(field_on_page);
3348     }
3349   while (field_on_page->frow == frow && field_on_page->fcol != fcol);
3350 
3351   if (field_on_page->frow != frow)
3352     {
3353       /* We really found a 'previous' line. We are positioned at the
3354          rightmost field on this line */
3355       frow = field_on_page->frow;
3356 
3357       /* We walk to the left as long as we are really right of the
3358          field. */
3359       while (field_on_page->frow == frow && field_on_page->fcol > fcol)
3360 	field_on_page = Sorted_Previous_Field(field_on_page);
3361 
3362       /* If we wrapped, just go to the right which is the first field on
3363          the row */
3364       if (field_on_page->frow != frow)
3365 	field_on_page = Sorted_Next_Field(field_on_page);
3366     }
3367 
3368   return (field_on_page);
3369 }
3370 
3371 /*---------------------------------------------------------------------------
3372 |   Facility      :  libnform
3373 |   Function      :  static FIELD *Down_Neighbor_Field(FIELD * field)
3374 |
3375 |   Description   :  Because of the row-major nature of sorting the fields,
3376 |                    its more difficult to define whats the down neighbor
3377 |                    field really means. We define that it must be on a
3378 |                    'next' line (cyclic order!) and is the leftmost
3379 |                    field laying on the right side of the given field. If
3380 |                    this set is empty, we take the last field on the line.
3381 |
3382 |   Return Values :  Pointer to the upper neighbor field.
3383 +--------------------------------------------------------------------------*/
3384 static FIELD *
3385 Down_Neighbor_Field(FIELD *field)
3386 {
3387   FIELD *field_on_page = field;
3388   int frow = field->frow;
3389   int fcol = field->fcol;
3390 
3391   /* Walk forward to the 'next' line. The second term in the while clause
3392      just guarantees that we stop if we cycled through the line because
3393      there might be no 'next' line if the page has just one line.
3394    */
3395   do
3396     {
3397       field_on_page = Sorted_Next_Field(field_on_page);
3398     }
3399   while (field_on_page->frow == frow && field_on_page->fcol != fcol);
3400 
3401   if (field_on_page->frow != frow)
3402     {
3403       /* We really found a 'next' line. We are positioned at the rightmost
3404          field on this line */
3405       frow = field_on_page->frow;
3406 
3407       /* We walk to the right as long as we are really left of the
3408          field. */
3409       while (field_on_page->frow == frow && field_on_page->fcol < fcol)
3410 	field_on_page = Sorted_Next_Field(field_on_page);
3411 
3412       /* If we wrapped, just go to the left which is the last field on
3413          the row */
3414       if (field_on_page->frow != frow)
3415 	field_on_page = Sorted_Previous_Field(field_on_page);
3416     }
3417 
3418   return (field_on_page);
3419 }
3420 
3421 /*----------------------------------------------------------------------------
3422   Inter-Field Navigation routines
3423   --------------------------------------------------------------------------*/
3424 
3425 /*---------------------------------------------------------------------------
3426 |   Facility      :  libnform
3427 |   Function      :  static int Inter_Field_Navigation(
3428 |                                           int (* const fct) (FORM *),
3429 |                                           FORM * form)
3430 |
3431 |   Description   :  Generic behavior for changing the current field, the
3432 |                    field is left and a new field is entered. So the field
3433 |                    must be validated and the field init/term hooks must
3434 |                    be called.
3435 |
3436 |   Return Values :  E_OK                - success
3437 |                    E_INVALID_FIELD     - field is invalid
3438 |                    some other          - error from subordinate call
3439 +--------------------------------------------------------------------------*/
3440 static int
3441 Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form)
3442 {
3443   int res;
3444 
3445   if (!_nc_Internal_Validation(form))
3446     res = E_INVALID_FIELD;
3447   else
3448     {
3449       Call_Hook(form, fieldterm);
3450       res = fct(form);
3451       Call_Hook(form, fieldinit);
3452     }
3453   return res;
3454 }
3455 
3456 /*---------------------------------------------------------------------------
3457 |   Facility      :  libnform
3458 |   Function      :  static int FN_Next_Field(FORM * form)
3459 |
3460 |   Description   :  Move to the next field on the current page of the form
3461 |
3462 |   Return Values :  E_OK                 - success
3463 |                    != E_OK              - error from subordinate call
3464 +--------------------------------------------------------------------------*/
3465 static int
3466 FN_Next_Field(FORM *form)
3467 {
3468   T((T_CALLED("FN_Next_Field(%p)"), form));
3469   returnCode(_nc_Set_Current_Field(form,
3470 				   Next_Field_On_Page(form->current)));
3471 }
3472 
3473 /*---------------------------------------------------------------------------
3474 |   Facility      :  libnform
3475 |   Function      :  static int FN_Previous_Field(FORM * form)
3476 |
3477 |   Description   :  Move to the previous field on the current page of the
3478 |                    form
3479 |
3480 |   Return Values :  E_OK                 - success
3481 |                    != E_OK              - error from subordinate call
3482 +--------------------------------------------------------------------------*/
3483 static int
3484 FN_Previous_Field(FORM *form)
3485 {
3486   T((T_CALLED("FN_Previous_Field(%p)"), form));
3487   returnCode(_nc_Set_Current_Field(form,
3488 				   Previous_Field_On_Page(form->current)));
3489 }
3490 
3491 /*---------------------------------------------------------------------------
3492 |   Facility      :  libnform
3493 |   Function      :  static int FN_First_Field(FORM * form)
3494 |
3495 |   Description   :  Move to the first field on the current page of the form
3496 |
3497 |   Return Values :  E_OK                 - success
3498 |                    != E_OK              - error from subordinate call
3499 +--------------------------------------------------------------------------*/
3500 static int
3501 FN_First_Field(FORM *form)
3502 {
3503   T((T_CALLED("FN_First_Field(%p)"), form));
3504   returnCode(_nc_Set_Current_Field(form,
3505 				   Next_Field_On_Page(form->field[form->page[form->curpage].pmax])));
3506 }
3507 
3508 /*---------------------------------------------------------------------------
3509 |   Facility      :  libnform
3510 |   Function      :  static int FN_Last_Field(FORM * form)
3511 |
3512 |   Description   :  Move to the last field on the current page of the form
3513 |
3514 |   Return Values :  E_OK                 - success
3515 |                    != E_OK              - error from subordinate call
3516 +--------------------------------------------------------------------------*/
3517 static int
3518 FN_Last_Field(FORM *form)
3519 {
3520   T((T_CALLED("FN_Last_Field(%p)"), form));
3521   returnCode(
3522 	      _nc_Set_Current_Field(form,
3523 				    Previous_Field_On_Page(form->field[form->page[form->curpage].pmin])));
3524 }
3525 
3526 /*---------------------------------------------------------------------------
3527 |   Facility      :  libnform
3528 |   Function      :  static int FN_Sorted_Next_Field(FORM * form)
3529 |
3530 |   Description   :  Move to the sorted next field on the current page
3531 |                    of the form.
3532 |
3533 |   Return Values :  E_OK            - success
3534 |                    != E_OK         - error from subordinate call
3535 +--------------------------------------------------------------------------*/
3536 static int
3537 FN_Sorted_Next_Field(FORM *form)
3538 {
3539   T((T_CALLED("FN_Sorted_Next_Field(%p)"), form));
3540   returnCode(_nc_Set_Current_Field(form,
3541 				   Sorted_Next_Field(form->current)));
3542 }
3543 
3544 /*---------------------------------------------------------------------------
3545 |   Facility      :  libnform
3546 |   Function      :  static int FN_Sorted_Previous_Field(FORM * form)
3547 |
3548 |   Description   :  Move to the sorted previous field on the current page
3549 |                    of the form.
3550 |
3551 |   Return Values :  E_OK            - success
3552 |                    != E_OK         - error from subordinate call
3553 +--------------------------------------------------------------------------*/
3554 static int
3555 FN_Sorted_Previous_Field(FORM *form)
3556 {
3557   T((T_CALLED("FN_Sorted_Previous_Field(%p)"), form));
3558   returnCode(_nc_Set_Current_Field(form,
3559 				   Sorted_Previous_Field(form->current)));
3560 }
3561 
3562 /*---------------------------------------------------------------------------
3563 |   Facility      :  libnform
3564 |   Function      :  static int FN_Sorted_First_Field(FORM * form)
3565 |
3566 |   Description   :  Move to the sorted first field on the current page
3567 |                    of the form.
3568 |
3569 |   Return Values :  E_OK            - success
3570 |                    != E_OK         - error from subordinate call
3571 +--------------------------------------------------------------------------*/
3572 static int
3573 FN_Sorted_First_Field(FORM *form)
3574 {
3575   T((T_CALLED("FN_Sorted_First_Field(%p)"), form));
3576   returnCode(_nc_Set_Current_Field(form,
3577 				   Sorted_Next_Field(form->field[form->page[form->curpage].smax])));
3578 }
3579 
3580 /*---------------------------------------------------------------------------
3581 |   Facility      :  libnform
3582 |   Function      :  static int FN_Sorted_Last_Field(FORM * form)
3583 |
3584 |   Description   :  Move to the sorted last field on the current page
3585 |                    of the form.
3586 |
3587 |   Return Values :  E_OK            - success
3588 |                    != E_OK         - error from subordinate call
3589 +--------------------------------------------------------------------------*/
3590 static int
3591 FN_Sorted_Last_Field(FORM *form)
3592 {
3593   T((T_CALLED("FN_Sorted_Last_Field(%p)"), form));
3594   returnCode(_nc_Set_Current_Field(form,
3595 				   Sorted_Previous_Field(form->field[form->page[form->curpage].smin])));
3596 }
3597 
3598 /*---------------------------------------------------------------------------
3599 |   Facility      :  libnform
3600 |   Function      :  static int FN_Left_Field(FORM * form)
3601 |
3602 |   Description   :  Get the field on the left of the current field on the
3603 |                    same line and the same page. Cycles through the line.
3604 |
3605 |   Return Values :  E_OK            - success
3606 |                    != E_OK         - error from subordinate call
3607 +--------------------------------------------------------------------------*/
3608 static int
3609 FN_Left_Field(FORM *form)
3610 {
3611   T((T_CALLED("FN_Left_Field(%p)"), form));
3612   returnCode(_nc_Set_Current_Field(form,
3613 				   Left_Neighbor_Field(form->current)));
3614 }
3615 
3616 /*---------------------------------------------------------------------------
3617 |   Facility      :  libnform
3618 |   Function      :  static int FN_Right_Field(FORM * form)
3619 |
3620 |   Description   :  Get the field on the right of the current field on the
3621 |                    same line and the same page. Cycles through the line.
3622 |
3623 |   Return Values :  E_OK            - success
3624 |                    != E_OK         - error from subordinate call
3625 +--------------------------------------------------------------------------*/
3626 static int
3627 FN_Right_Field(FORM *form)
3628 {
3629   T((T_CALLED("FN_Right_Field(%p)"), form));
3630   returnCode(_nc_Set_Current_Field(form,
3631 				   Right_Neighbor_Field(form->current)));
3632 }
3633 
3634 /*---------------------------------------------------------------------------
3635 |   Facility      :  libnform
3636 |   Function      :  static int FN_Up_Field(FORM * form)
3637 |
3638 |   Description   :  Get the upper neighbor of the current field. This
3639 |                    cycles through the page. See the comments of the
3640 |                    Upper_Neighbor_Field function to understand how
3641 |                    'upper' is defined.
3642 |
3643 |   Return Values :  E_OK            - success
3644 |                    != E_OK         - error from subordinate call
3645 +--------------------------------------------------------------------------*/
3646 static int
3647 FN_Up_Field(FORM *form)
3648 {
3649   T((T_CALLED("FN_Up_Field(%p)"), form));
3650   returnCode(_nc_Set_Current_Field(form,
3651 				   Upper_Neighbor_Field(form->current)));
3652 }
3653 
3654 /*---------------------------------------------------------------------------
3655 |   Facility      :  libnform
3656 |   Function      :  static int FN_Down_Field(FORM * form)
3657 |
3658 |   Description   :  Get the down neighbor of the current field. This
3659 |                    cycles through the page. See the comments of the
3660 |                    Down_Neighbor_Field function to understand how
3661 |                    'down' is defined.
3662 |
3663 |   Return Values :  E_OK            - success
3664 |                    != E_OK         - error from subordinate call
3665 +--------------------------------------------------------------------------*/
3666 static int
3667 FN_Down_Field(FORM *form)
3668 {
3669   T((T_CALLED("FN_Down_Field(%p)"), form));
3670   returnCode(_nc_Set_Current_Field(form,
3671 				   Down_Neighbor_Field(form->current)));
3672 }
3673 /*----------------------------------------------------------------------------
3674   END of Field Navigation routines
3675   --------------------------------------------------------------------------*/
3676 
3677 /*----------------------------------------------------------------------------
3678   Helper routines for Page Navigation
3679   --------------------------------------------------------------------------*/
3680 
3681 /*---------------------------------------------------------------------------
3682 |   Facility      :  libnform
3683 |   Function      :  int _nc_Set_Form_Page(FORM * form,
3684 |                                          int page,
3685 |                                          FIELD * field)
3686 |
3687 |   Description   :  Make the given page number the current page and make
3688 |                    the given field the current field on the page. If
3689 |                    for the field NULL is given, make the first field on
3690 |                    the page the current field. The routine acts only
3691 |                    if the requested page is not the current page.
3692 |
3693 |   Return Values :  E_OK                - success
3694 |                    != E_OK             - error from subordinate call
3695 |                    E_BAD_ARGUMENT      - invalid field pointer
3696 |                    E_SYSTEM_ERROR      - some severe basic error
3697 +--------------------------------------------------------------------------*/
3698 NCURSES_EXPORT(int)
3699 _nc_Set_Form_Page(FORM *form, int page, FIELD *field)
3700 {
3701   int res = E_OK;
3702 
3703   if ((form->curpage != page))
3704     {
3705       FIELD *last_field, *field_on_page;
3706 
3707       werase(Get_Form_Window(form));
3708       form->curpage = page;
3709       last_field = field_on_page = form->field[form->page[page].smin];
3710       do
3711 	{
3712 	  if (field_on_page->opts & O_VISIBLE)
3713 	    if ((res = Display_Field(field_on_page)) != E_OK)
3714 	      return (res);
3715 	  field_on_page = field_on_page->snext;
3716 	}
3717       while (field_on_page != last_field);
3718 
3719       if (field)
3720 	res = _nc_Set_Current_Field(form, field);
3721       else
3722 	/* N.B.: we don't encapsulate this by Inter_Field_Navigation(),
3723 	   because this is already executed in a page navigation
3724 	   context that contains field navigation
3725 	 */
3726 	res = FN_First_Field(form);
3727     }
3728   return (res);
3729 }
3730 
3731 /*---------------------------------------------------------------------------
3732 |   Facility      :  libnform
3733 |   Function      :  static int Next_Page_Number(const FORM * form)
3734 |
3735 |   Description   :  Calculate the page number following the current page
3736 |                    number. This cycles if the highest page number is
3737 |                    reached.
3738 |
3739 |   Return Values :  The next page number
3740 +--------------------------------------------------------------------------*/
3741 NCURSES_INLINE static int
3742 Next_Page_Number(const FORM *form)
3743 {
3744   return (form->curpage + 1) % form->maxpage;
3745 }
3746 
3747 /*---------------------------------------------------------------------------
3748 |   Facility      :  libnform
3749 |   Function      :  static int Previous_Page_Number(const FORM * form)
3750 |
3751 |   Description   :  Calculate the page number before the current page
3752 |                    number. This cycles if the first page number is
3753 |                    reached.
3754 |
3755 |   Return Values :  The previous page number
3756 +--------------------------------------------------------------------------*/
3757 NCURSES_INLINE static int
3758 Previous_Page_Number(const FORM *form)
3759 {
3760   return (form->curpage != 0 ? form->curpage - 1 : form->maxpage - 1);
3761 }
3762 
3763 /*----------------------------------------------------------------------------
3764   Page Navigation routines
3765   --------------------------------------------------------------------------*/
3766 
3767 /*---------------------------------------------------------------------------
3768 |   Facility      :  libnform
3769 |   Function      :  static int Page_Navigation(
3770 |                                               int (* const fct) (FORM *),
3771 |                                               FORM * form)
3772 |
3773 |   Description   :  Generic behavior for changing a page. This means
3774 |                    that the field is left and a new field is entered.
3775 |                    So the field must be validated and the field init/term
3776 |                    hooks must be called. Because also the page is changed,
3777 |                    the forms init/term hooks must be called also.
3778 |
3779 |   Return Values :  E_OK                - success
3780 |                    E_INVALID_FIELD     - field is invalid
3781 |                    some other          - error from subordinate call
3782 +--------------------------------------------------------------------------*/
3783 static int
3784 Page_Navigation(int (*const fct) (FORM *), FORM *form)
3785 {
3786   int res;
3787 
3788   if (!_nc_Internal_Validation(form))
3789     res = E_INVALID_FIELD;
3790   else
3791     {
3792       Call_Hook(form, fieldterm);
3793       Call_Hook(form, formterm);
3794       res = fct(form);
3795       Call_Hook(form, forminit);
3796       Call_Hook(form, fieldinit);
3797     }
3798   return res;
3799 }
3800 
3801 /*---------------------------------------------------------------------------
3802 |   Facility      :  libnform
3803 |   Function      :  static int PN_Next_Page(FORM * form)
3804 |
3805 |   Description   :  Move to the next page of the form
3806 |
3807 |   Return Values :  E_OK                - success
3808 |                    != E_OK             - error from subordinate call
3809 +--------------------------------------------------------------------------*/
3810 static int
3811 PN_Next_Page(FORM *form)
3812 {
3813   T((T_CALLED("PN_Next_Page(%p)"), form));
3814   returnCode(_nc_Set_Form_Page(form, Next_Page_Number(form), (FIELD *)0));
3815 }
3816 
3817 /*---------------------------------------------------------------------------
3818 |   Facility      :  libnform
3819 |   Function      :  static int PN_Previous_Page(FORM * form)
3820 |
3821 |   Description   :  Move to the previous page of the form
3822 |
3823 |   Return Values :  E_OK              - success
3824 |                    != E_OK           - error from subordinate call
3825 +--------------------------------------------------------------------------*/
3826 static int
3827 PN_Previous_Page(FORM *form)
3828 {
3829   T((T_CALLED("PN_Previous_Page(%p)"), form));
3830   returnCode(_nc_Set_Form_Page(form, Previous_Page_Number(form), (FIELD *)0));
3831 }
3832 
3833 /*---------------------------------------------------------------------------
3834 |   Facility      :  libnform
3835 |   Function      :  static int PN_First_Page(FORM * form)
3836 |
3837 |   Description   :  Move to the first page of the form
3838 |
3839 |   Return Values :  E_OK              - success
3840 |                    != E_OK           - error from subordinate call
3841 +--------------------------------------------------------------------------*/
3842 static int
3843 PN_First_Page(FORM *form)
3844 {
3845   T((T_CALLED("PN_First_Page(%p)"), form));
3846   returnCode(_nc_Set_Form_Page(form, 0, (FIELD *)0));
3847 }
3848 
3849 /*---------------------------------------------------------------------------
3850 |   Facility      :  libnform
3851 |   Function      :  static int PN_Last_Page(FORM * form)
3852 |
3853 |   Description   :  Move to the last page of the form
3854 |
3855 |   Return Values :  E_OK              - success
3856 |                    != E_OK           - error from subordinate call
3857 +--------------------------------------------------------------------------*/
3858 static int
3859 PN_Last_Page(FORM *form)
3860 {
3861   T((T_CALLED("PN_Last_Page(%p)"), form));
3862   returnCode(_nc_Set_Form_Page(form, form->maxpage - 1, (FIELD *)0));
3863 }
3864 
3865 /*----------------------------------------------------------------------------
3866   END of Field Navigation routines
3867   --------------------------------------------------------------------------*/
3868 
3869 /*----------------------------------------------------------------------------
3870   Helper routines for the core form driver.
3871   --------------------------------------------------------------------------*/
3872 
3873 /*---------------------------------------------------------------------------
3874 |   Facility      :  libnform
3875 |   Function      :  static int Data_Entry(FORM * form,int c)
3876 |
3877 |   Description   :  Enter character c into at the current position of the
3878 |                    current field of the form.
3879 |
3880 |   Return Values :  E_OK              - success
3881 |                    E_REQUEST_DENIED  - driver could not process the request
3882 |                    E_SYSTEM_ERROR    -
3883 +--------------------------------------------------------------------------*/
3884 static int
3885 Data_Entry(FORM *form, int c)
3886 {
3887   FIELD *field = form->current;
3888   int result = E_REQUEST_DENIED;
3889 
3890   T((T_CALLED("Data_Entry(%p,%s)"), form, _tracechtype((chtype)c)));
3891   if ((field->opts & O_EDIT)
3892 #if FIX_FORM_INACTIVE_BUG
3893       && (field->opts & O_ACTIVE)
3894 #endif
3895     )
3896     {
3897       if ((field->opts & O_BLANK) &&
3898 	  First_Position_In_Current_Field(form) &&
3899 	  !(form->status & _FCHECK_REQUIRED) &&
3900 	  !(form->status & _WINDOW_MODIFIED))
3901 	werase(form->w);
3902 
3903       if (form->status & _OVLMODE)
3904 	{
3905 	  waddch(form->w, (chtype)c);
3906 	}
3907       else
3908 	/* no _OVLMODE */
3909 	{
3910 	  bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
3911 
3912 	  if (!(There_Is_Room ||
3913 		((Single_Line_Field(field) && Growable(field)))))
3914 	    RETURN(E_REQUEST_DENIED);
3915 
3916 	  if (!There_Is_Room && !Field_Grown(field, 1))
3917 	    RETURN(E_SYSTEM_ERROR);
3918 
3919 	  winsch(form->w, (chtype)c);
3920 	}
3921 
3922       if ((result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form)) == E_OK)
3923 	{
3924 	  bool End_Of_Field = (((field->drows - 1) == form->currow) &&
3925 			       ((field->dcols - 1) == form->curcol));
3926 
3927 	  form->status |= _WINDOW_MODIFIED;
3928 	  if (End_Of_Field && !Growable(field) && (field->opts & O_AUTOSKIP))
3929 	    result = Inter_Field_Navigation(FN_Next_Field, form);
3930 	  else
3931 	    {
3932 	      if (End_Of_Field && Growable(field) && !Field_Grown(field, 1))
3933 		result = E_SYSTEM_ERROR;
3934 	      else
3935 		{
3936 #if USE_WIDEC_SUPPORT
3937 		  /*
3938 		   * We have just added a byte to the form field.  It may have
3939 		   * been part of a multibyte character.  If it was, the
3940 		   * addch_used field is nonzero and we should not try to move
3941 		   * to a new column.
3942 		   */
3943 		  if (WINDOW_EXT(form->w, addch_used) == 0)
3944 		    IFN_Next_Character(form);
3945 #else
3946 		  IFN_Next_Character(form);
3947 #endif
3948 		  result = E_OK;
3949 		}
3950 	    }
3951 	}
3952     }
3953   RETURN(result);
3954 }
3955 
3956 /* Structure to describe the binding of a request code to a function.
3957    The member keycode codes the request value as well as the generic
3958    routine to use for the request. The code for the generic routine
3959    is coded in the upper 16 Bits while the request code is coded in
3960    the lower 16 bits.
3961 
3962    In terms of C++ you might think of a request as a class with a
3963    virtual method "perform". The different types of request are
3964    derived from this base class and overload (or not) the base class
3965    implementation of perform.
3966 */
3967 typedef struct
3968 {
3969   int keycode;			/* must be at least 32 bit: hi:mode, lo: key */
3970   int (*cmd) (FORM *);		/* low level driver routine for this key     */
3971 }
3972 Binding_Info;
3973 
3974 /* You may see this is the class-id of the request type class */
3975 #define ID_PN    (0x00000000)	/* Page navigation           */
3976 #define ID_FN    (0x00010000)	/* Inter-Field navigation    */
3977 #define ID_IFN   (0x00020000)	/* Intra-Field navigation    */
3978 #define ID_VSC   (0x00030000)	/* Vertical Scrolling        */
3979 #define ID_HSC   (0x00040000)	/* Horizontal Scrolling      */
3980 #define ID_FE    (0x00050000)	/* Field Editing             */
3981 #define ID_EM    (0x00060000)	/* Edit Mode                 */
3982 #define ID_FV    (0x00070000)	/* Field Validation          */
3983 #define ID_CH    (0x00080000)	/* Choice                    */
3984 #define ID_Mask  (0xffff0000)
3985 #define Key_Mask (0x0000ffff)
3986 #define ID_Shft  (16)
3987 
3988 /* This array holds all the Binding Infos */
3989 /* *INDENT-OFF* */
3990 static const Binding_Info bindings[MAX_FORM_COMMAND - MIN_FORM_COMMAND + 1] =
3991 {
3992   { REQ_NEXT_PAGE    |ID_PN  ,PN_Next_Page},
3993   { REQ_PREV_PAGE    |ID_PN  ,PN_Previous_Page},
3994   { REQ_FIRST_PAGE   |ID_PN  ,PN_First_Page},
3995   { REQ_LAST_PAGE    |ID_PN  ,PN_Last_Page},
3996 
3997   { REQ_NEXT_FIELD   |ID_FN  ,FN_Next_Field},
3998   { REQ_PREV_FIELD   |ID_FN  ,FN_Previous_Field},
3999   { REQ_FIRST_FIELD  |ID_FN  ,FN_First_Field},
4000   { REQ_LAST_FIELD   |ID_FN  ,FN_Last_Field},
4001   { REQ_SNEXT_FIELD  |ID_FN  ,FN_Sorted_Next_Field},
4002   { REQ_SPREV_FIELD  |ID_FN  ,FN_Sorted_Previous_Field},
4003   { REQ_SFIRST_FIELD |ID_FN  ,FN_Sorted_First_Field},
4004   { REQ_SLAST_FIELD  |ID_FN  ,FN_Sorted_Last_Field},
4005   { REQ_LEFT_FIELD   |ID_FN  ,FN_Left_Field},
4006   { REQ_RIGHT_FIELD  |ID_FN  ,FN_Right_Field},
4007   { REQ_UP_FIELD     |ID_FN  ,FN_Up_Field},
4008   { REQ_DOWN_FIELD   |ID_FN  ,FN_Down_Field},
4009 
4010   { REQ_NEXT_CHAR    |ID_IFN ,IFN_Next_Character},
4011   { REQ_PREV_CHAR    |ID_IFN ,IFN_Previous_Character},
4012   { REQ_NEXT_LINE    |ID_IFN ,IFN_Next_Line},
4013   { REQ_PREV_LINE    |ID_IFN ,IFN_Previous_Line},
4014   { REQ_NEXT_WORD    |ID_IFN ,IFN_Next_Word},
4015   { REQ_PREV_WORD    |ID_IFN ,IFN_Previous_Word},
4016   { REQ_BEG_FIELD    |ID_IFN ,IFN_Beginning_Of_Field},
4017   { REQ_END_FIELD    |ID_IFN ,IFN_End_Of_Field},
4018   { REQ_BEG_LINE     |ID_IFN ,IFN_Beginning_Of_Line},
4019   { REQ_END_LINE     |ID_IFN ,IFN_End_Of_Line},
4020   { REQ_LEFT_CHAR    |ID_IFN ,IFN_Left_Character},
4021   { REQ_RIGHT_CHAR   |ID_IFN ,IFN_Right_Character},
4022   { REQ_UP_CHAR      |ID_IFN ,IFN_Up_Character},
4023   { REQ_DOWN_CHAR    |ID_IFN ,IFN_Down_Character},
4024 
4025   { REQ_NEW_LINE     |ID_FE  ,FE_New_Line},
4026   { REQ_INS_CHAR     |ID_FE  ,FE_Insert_Character},
4027   { REQ_INS_LINE     |ID_FE  ,FE_Insert_Line},
4028   { REQ_DEL_CHAR     |ID_FE  ,FE_Delete_Character},
4029   { REQ_DEL_PREV     |ID_FE  ,FE_Delete_Previous},
4030   { REQ_DEL_LINE     |ID_FE  ,FE_Delete_Line},
4031   { REQ_DEL_WORD     |ID_FE  ,FE_Delete_Word},
4032   { REQ_CLR_EOL      |ID_FE  ,FE_Clear_To_End_Of_Line},
4033   { REQ_CLR_EOF      |ID_FE  ,FE_Clear_To_End_Of_Field},
4034   { REQ_CLR_FIELD    |ID_FE  ,FE_Clear_Field},
4035 
4036   { REQ_OVL_MODE     |ID_EM  ,EM_Overlay_Mode},
4037   { REQ_INS_MODE     |ID_EM  ,EM_Insert_Mode},
4038 
4039   { REQ_SCR_FLINE    |ID_VSC ,VSC_Scroll_Line_Forward},
4040   { REQ_SCR_BLINE    |ID_VSC ,VSC_Scroll_Line_Backward},
4041   { REQ_SCR_FPAGE    |ID_VSC ,VSC_Scroll_Page_Forward},
4042   { REQ_SCR_BPAGE    |ID_VSC ,VSC_Scroll_Page_Backward},
4043   { REQ_SCR_FHPAGE   |ID_VSC ,VSC_Scroll_Half_Page_Forward},
4044   { REQ_SCR_BHPAGE   |ID_VSC ,VSC_Scroll_Half_Page_Backward},
4045 
4046   { REQ_SCR_FCHAR    |ID_HSC ,HSC_Scroll_Char_Forward},
4047   { REQ_SCR_BCHAR    |ID_HSC ,HSC_Scroll_Char_Backward},
4048   { REQ_SCR_HFLINE   |ID_HSC ,HSC_Horizontal_Line_Forward},
4049   { REQ_SCR_HBLINE   |ID_HSC ,HSC_Horizontal_Line_Backward},
4050   { REQ_SCR_HFHALF   |ID_HSC ,HSC_Horizontal_Half_Line_Forward},
4051   { REQ_SCR_HBHALF   |ID_HSC ,HSC_Horizontal_Half_Line_Backward},
4052 
4053   { REQ_VALIDATION   |ID_FV  ,FV_Validation},
4054 
4055   { REQ_NEXT_CHOICE  |ID_CH  ,CR_Next_Choice},
4056   { REQ_PREV_CHOICE  |ID_CH  ,CR_Previous_Choice}
4057 };
4058 /* *INDENT-ON* */
4059 
4060 /*---------------------------------------------------------------------------
4061 |   Facility      :  libnform
4062 |   Function      :  int form_driver(FORM * form,int  c)
4063 |
4064 |   Description   :  This is the workhorse of the forms system. It checks
4065 |                    to determine whether the character c is a request or
4066 |                    data. If it is a request, the form driver executes
4067 |                    the request and returns the result. If it is data
4068 |                    (printable character), it enters the data into the
4069 |                    current position in the current field. If it is not
4070 |                    recognized, the form driver assumes it is an application
4071 |                    defined command and returns E_UNKNOWN_COMMAND.
4072 |                    Application defined command should be defined relative
4073 |                    to MAX_FORM_COMMAND, the maximum value of a request.
4074 |
4075 |   Return Values :  E_OK              - success
4076 |                    E_SYSTEM_ERROR    - system error
4077 |                    E_BAD_ARGUMENT    - an argument is incorrect
4078 |                    E_NOT_POSTED      - form is not posted
4079 |                    E_INVALID_FIELD   - field contents are invalid
4080 |                    E_BAD_STATE       - called from inside a hook routine
4081 |                    E_REQUEST_DENIED  - request failed
4082 |                    E_NOT_CONNECTED   - no fields are connected to the form
4083 |                    E_UNKNOWN_COMMAND - command not known
4084 +--------------------------------------------------------------------------*/
4085 NCURSES_EXPORT(int)
4086 form_driver(FORM *form, int c)
4087 {
4088   const Binding_Info *BI = (Binding_Info *) 0;
4089   int res = E_UNKNOWN_COMMAND;
4090 
4091   T((T_CALLED("form_driver(%p,%d)"), form, c));
4092 
4093   if (!form)
4094     RETURN(E_BAD_ARGUMENT);
4095 
4096   if (!(form->field))
4097     RETURN(E_NOT_CONNECTED);
4098 
4099   assert(form->page);
4100 
4101   if (c == FIRST_ACTIVE_MAGIC)
4102     {
4103       form->current = _nc_First_Active_Field(form);
4104       RETURN(E_OK);
4105     }
4106 
4107   assert(form->current &&
4108 	 form->current->buf &&
4109 	 (form->current->form == form)
4110     );
4111 
4112   if (form->status & _IN_DRIVER)
4113     RETURN(E_BAD_STATE);
4114 
4115   if (!(form->status & _POSTED))
4116     RETURN(E_NOT_POSTED);
4117 
4118   if ((c >= MIN_FORM_COMMAND && c <= MAX_FORM_COMMAND) &&
4119       ((bindings[c - MIN_FORM_COMMAND].keycode & Key_Mask) == c))
4120     BI = &(bindings[c - MIN_FORM_COMMAND]);
4121 
4122   if (BI)
4123     {
4124       typedef int (*Generic_Method) (int (*const) (FORM *), FORM *);
4125       static const Generic_Method Generic_Methods[] =
4126       {
4127 	Page_Navigation,	/* overloaded to call field&form hooks */
4128 	Inter_Field_Navigation,	/* overloaded to call field hooks      */
4129 	NULL,			/* Intra-Field is generic              */
4130 	Vertical_Scrolling,	/* Overloaded to check multi-line      */
4131 	Horizontal_Scrolling,	/* Overloaded to check single-line     */
4132 	Field_Editing,		/* Overloaded to mark modification     */
4133 	NULL,			/* Edit Mode is generic                */
4134 	NULL,			/* Field Validation is generic         */
4135 	NULL			/* Choice Request is generic           */
4136       };
4137       size_t nMethods = (sizeof(Generic_Methods) / sizeof(Generic_Methods[0]));
4138       size_t method = ((BI->keycode & ID_Mask) >> ID_Shft) & 0xffff;
4139 
4140       if ((method >= nMethods) || !(BI->cmd))
4141 	res = E_SYSTEM_ERROR;
4142       else
4143 	{
4144 	  Generic_Method fct = Generic_Methods[method];
4145 
4146 	  if (fct)
4147 	    res = fct(BI->cmd, form);
4148 	  else
4149 	    res = (BI->cmd) (form);
4150 	}
4151     }
4152   else if (!(c & (~(int)MAX_REGULAR_CHARACTER)))
4153     {
4154       /*
4155        * If we're using 8-bit characters, iscntrl+isprint cover the whole set.
4156        * But with multibyte characters, there is a third possibility, i.e.,
4157        * parts of characters that build up into printable characters which are
4158        * not considered printable.
4159        *
4160        * FIXME: the wide-character branch should also use Check_Char().
4161        */
4162 #if USE_WIDEC_SUPPORT
4163       if (!iscntrl(UChar(c)))
4164 #else
4165       if (isprint(UChar(c)) &&
4166 	  Check_Char(form->current->type, c,
4167 		     (TypeArgument *)(form->current->arg)))
4168 #endif
4169 	res = Data_Entry(form, c);
4170     }
4171   _nc_Refresh_Current_Field(form);
4172   RETURN(res);
4173 }
4174 
4175 /*----------------------------------------------------------------------------
4176   Field-Buffer manipulation routines.
4177   The effects of setting a buffer are tightly coupled to the core of the form
4178   driver logic. This is especially true in the case of growable fields.
4179   So I don't separate this into a separate module.
4180   --------------------------------------------------------------------------*/
4181 
4182 /*---------------------------------------------------------------------------
4183 |   Facility      :  libnform
4184 |   Function      :  int set_field_buffer(FIELD *field,
4185 |                                         int buffer, char *value)
4186 |
4187 |   Description   :  Set the given buffer of the field to the given value.
4188 |                    Buffer 0 stores the displayed content of the field.
4189 |                    For dynamic fields this may grow the fieldbuffers if
4190 |                    the length of the value exceeds the current buffer
4191 |                    length. For buffer 0 only printable values are allowed.
4192 |                    For static fields, the value needs not to be zero ter-
4193 |                    minated. It is copied up to the length of the buffer.
4194 |
4195 |   Return Values :  E_OK            - success
4196 |                    E_BAD_ARGUMENT  - invalid argument
4197 |                    E_SYSTEM_ERROR  - system error
4198 +--------------------------------------------------------------------------*/
4199 NCURSES_EXPORT(int)
4200 set_field_buffer(FIELD *field, int buffer, const char *value)
4201 {
4202   FIELD_CELL *p;
4203   int res = E_OK;
4204   unsigned int i;
4205   unsigned int len;
4206 
4207 #if USE_WIDEC_SUPPORT
4208   FIELD_CELL *widevalue = 0;
4209 #endif
4210 
4211   T((T_CALLED("set_field_buffer(%p,%d,%s)"), field, buffer, _nc_visbuf(value)));
4212 
4213   if (!field || !value || ((buffer < 0) || (buffer > field->nbuf)))
4214     RETURN(E_BAD_ARGUMENT);
4215 
4216   len = Buffer_Length(field);
4217 
4218   if (buffer == 0)
4219     {
4220       for (i = 0; (value[i] != '\0') && (i < len); ++i)
4221 	{
4222 	  if (iscntrl(UChar(value[i])))
4223 	    RETURN(E_BAD_ARGUMENT);
4224 	}
4225     }
4226 
4227   if (Growable(field))
4228     {
4229       /* for a growable field we must assume zero terminated strings, because
4230          somehow we have to detect the length of what should be copied.
4231        */
4232       unsigned int vlen = strlen(value);
4233 
4234       if (vlen > len)
4235 	{
4236 	  if (!Field_Grown(field,
4237 			   (int)(1 + (vlen - len) / ((field->rows + field->nrow)
4238 						     * field->cols))))
4239 	    RETURN(E_SYSTEM_ERROR);
4240 
4241 	  /* in this case we also have to check, whether or not the remaining
4242 	     characters in value are also printable for buffer 0. */
4243 	  if (buffer == 0)
4244 	    {
4245 	      for (i = len; i < vlen; i++)
4246 		if (iscntrl(UChar(value[i])))
4247 		  RETURN(E_BAD_ARGUMENT);
4248 	    }
4249 	  len = vlen;
4250 	}
4251     }
4252 
4253   p = Address_Of_Nth_Buffer(field, buffer);
4254 
4255 #if USE_WIDEC_SUPPORT
4256   /*
4257    * Use addstr's logic for converting a string to an array of cchar_t's.
4258    * There should be a better way, but this handles nonspacing characters
4259    * and other special cases that we really do not want to handle here.
4260    */
4261   wclear(field->working);
4262   mvwaddstr(field->working, 0, 0, value);
4263 
4264   if ((widevalue = (FIELD_CELL *)calloc(len, sizeof(FIELD_CELL))) == 0)
4265     {
4266       RETURN(E_SYSTEM_ERROR);
4267     }
4268   else
4269     {
4270       mvwin_wchnstr(field->working, 0, 0, widevalue, (int)len);
4271       for (i = 0; i < len; ++i)
4272 	{
4273 	  if (CharEq(myZEROS, widevalue[i]))
4274 	    {
4275 	      while (i < len)
4276 		p[i++] = myBLANK;
4277 	      break;
4278 	    }
4279 	  p[i] = widevalue[i];
4280 	}
4281       free(widevalue);
4282     }
4283 #else
4284   for (i = 0; i < len; ++i)
4285     {
4286       if (value[i] == '\0')
4287 	{
4288 	  while (i < len)
4289 	    p[i++] = myBLANK;
4290 	  break;
4291 	}
4292       p[i] = value[i];
4293     }
4294 #endif
4295 
4296   if (buffer == 0)
4297     {
4298       int syncres;
4299 
4300       if (((syncres = Synchronize_Field(field)) != E_OK) &&
4301 	  (res == E_OK))
4302 	res = syncres;
4303       if (((syncres = Synchronize_Linked_Fields(field)) != E_OK) &&
4304 	  (res == E_OK))
4305 	res = syncres;
4306     }
4307   RETURN(res);
4308 }
4309 
4310 /*---------------------------------------------------------------------------
4311 |   Facility      :  libnform
4312 |   Function      :  char *field_buffer(const FIELD *field,int buffer)
4313 |
4314 |   Description   :  Return the address of the buffer for the field.
4315 |
4316 |   Return Values :  Pointer to buffer or NULL if arguments were invalid.
4317 +--------------------------------------------------------------------------*/
4318 NCURSES_EXPORT(char *)
4319 field_buffer(const FIELD *field, int buffer)
4320 {
4321   char *result = 0;
4322 
4323   T((T_CALLED("field_buffer(%p,%d)"), field, buffer));
4324 
4325   if (field && (buffer >= 0) && (buffer <= field->nbuf))
4326     {
4327 #if USE_WIDEC_SUPPORT
4328       FIELD_CELL *data = Address_Of_Nth_Buffer(field, buffer);
4329       unsigned need = 0;
4330       int size = Buffer_Length(field);
4331       int n;
4332 
4333       /* determine the number of bytes needed to store the expanded string */
4334       for (n = 0; n < size; ++n)
4335 	{
4336 	  if (!isWidecExt(data[n]))
4337 	    {
4338 	      mbstate_t state;
4339 	      size_t next;
4340 
4341 	      init_mb(state);
4342 	      next = _nc_wcrtomb(0, data[n].chars[0], &state);
4343 	      if (!isEILSEQ(next))
4344 		{
4345 		  if (next != 0)
4346 		    need += next;
4347 		}
4348 	    }
4349 	}
4350 
4351       /* allocate a place to store the expanded string */
4352       if (field->expanded[buffer] != 0)
4353 	free(field->expanded[buffer]);
4354       field->expanded[buffer] = typeMalloc(char, need + 1);
4355 
4356       /* expand the multibyte data */
4357       if ((result = field->expanded[buffer]) != 0)
4358 	{
4359 	  wclear(field->working);
4360 	  mvwadd_wchnstr(field->working, 0, 0, data, size);
4361 	  mvwinnstr(field->working, 0, 0, result, (int)need + 1);
4362 	}
4363 #else
4364       result = Address_Of_Nth_Buffer(field, buffer);
4365 #endif
4366     }
4367   returnPtr(result);
4368 }
4369 
4370 #if USE_WIDEC_SUPPORT
4371 
4372 /* FIXME: see lib_get_wch.c */
4373 #if HAVE_MBTOWC && HAVE_MBLEN
4374 #define reset_mbytes(state) mblen(NULL, 0), mbtowc(NULL, NULL, 0)
4375 #define count_mbytes(buffer,length,state) mblen(buffer,length)
4376 #define trans_mbytes(wch,buffer,length,state) \
4377 	(int) mbtowc(&wch, buffer, length)
4378 #elif HAVE_MBRTOWC && HAVE_MBRLEN
4379 #define NEED_STATE
4380 #define reset_mbytes(state) init_mb(state)
4381 #define count_mbytes(buffer,length,state) mbrlen(buffer,length,&state)
4382 #define trans_mbytes(wch,buffer,length,state) \
4383 	(int) mbrtowc(&wch, buffer, length, &state)
4384 #else
4385 make an error
4386 #endif
4387 
4388 /*---------------------------------------------------------------------------
4389 | Convert a multibyte string to a wide-character string.  The result must be
4390 | freed by the caller.
4391 +--------------------------------------------------------------------------*/
4392 NCURSES_EXPORT(wchar_t *)
4393 _nc_Widen_String(char *source, int *lengthp)
4394 {
4395   wchar_t *result = 0;
4396   wchar_t wch;
4397   size_t given = strlen(source);
4398   size_t tries;
4399   int pass;
4400   int status;
4401 
4402 #ifdef NEED_STATE
4403   mbstate_t state;
4404 #endif
4405 
4406   for (pass = 0; pass < 2; ++pass)
4407     {
4408       unsigned need = 0;
4409       size_t passed = 0;
4410 
4411       while (passed < given)
4412 	{
4413 	  bool found = FALSE;
4414 
4415 	  for (tries = 1, status = 0; tries <= (given - passed); ++tries)
4416 	    {
4417 	      int save = source[passed + tries];
4418 
4419 	      source[passed + tries] = 0;
4420 	      reset_mbytes(state);
4421 	      status = trans_mbytes(wch, source + passed, tries, state);
4422 	      source[passed + tries] = save;
4423 
4424 	      if (status > 0)
4425 		{
4426 		  found = TRUE;
4427 		  break;
4428 		}
4429 	    }
4430 	  if (found)
4431 	    {
4432 	      if (pass)
4433 		{
4434 		  result[need] = wch;
4435 		}
4436 	      passed += status;
4437 	      ++need;
4438 	    }
4439 	  else
4440 	    {
4441 	      if (pass)
4442 		{
4443 		  result[need] = source[passed];
4444 		}
4445 	      ++need;
4446 	      ++passed;
4447 	    }
4448 	}
4449 
4450       if (!pass)
4451 	{
4452 	  if (!need)
4453 	    break;
4454 	  result = typeCalloc(wchar_t, need);
4455 
4456 	  *lengthp = need;
4457 	  if (result == 0)
4458 	    break;
4459 	}
4460     }
4461 
4462   return result;
4463 }
4464 #endif
4465 
4466 /* frm_driver.c ends here */
4467