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