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