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