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