xref: /freebsd/contrib/ncurses/form/frm_driver.c (revision 68ad2b0d7af2a3571c4abac9afa712f9b09b721c)
1 /****************************************************************************
2  * Copyright 2018-2021,2024 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.137 2024/12/07 21:57:21 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       const 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   const 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   const 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   const 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 	      const 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 != NULL)
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 != NULL
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   const 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   const 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   const 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 != NULL);
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 == NULL))
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 = NULL;
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   const FIELD *field = form->current;
1706   FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
1707   FIELD_CELL *s;
1708   const 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   const FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
1749   const FIELD_CELL *s;
1750   const 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   const 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   const 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   const 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   const 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;
2369   const FIELD_CELL *s;
2370 
2371   Synchronize_Buffer(form);
2372   begin_of_last_line = Address_Of_Row_In_Buffer(field, (field->drows - 1));
2373   s = After_End_Of_Data(begin_of_last_line, field->dcols);
2374   return ((s == begin_of_last_line) ? TRUE : FALSE);
2375 }
2376 
2377 /*---------------------------------------------------------------------------
2378 |   Facility      :  libnform
2379 |   Function      :  static bool Is_There_Room_For_A_Char_In_Line(FORM * form)
2380 |
2381 |   Description   :  Checks whether or not there is room for a new character
2382 |                    in the current line.
2383 |
2384 |   Return Values :  TRUE    - there is room
2385 |                    FALSE   - there is not enough room (line full)
2386 +--------------------------------------------------------------------------*/
2387 NCURSES_INLINE static bool
Is_There_Room_For_A_Char_In_Line(FORM * form)2388 Is_There_Room_For_A_Char_In_Line(FORM *form)
2389 {
2390   int last_char_in_line;
2391 
2392   wmove(form->w, form->currow, form->current->dcols - 1);
2393   last_char_in_line = (int)(winch(form->w) & A_CHARTEXT);
2394   wmove(form->w, form->currow, form->curcol);
2395   return (((last_char_in_line == form->current->pad) ||
2396 	   is_blank(last_char_in_line)) ? TRUE : FALSE);
2397 }
2398 
2399 #define There_Is_No_Room_For_A_Char_In_Line(f) \
2400   !Is_There_Room_For_A_Char_In_Line(f)
2401 
2402 /*---------------------------------------------------------------------------
2403 |   Facility      :  libnform
2404 |   Function      :  static int Insert_String(
2405 |                                             FORM * form,
2406 |                                             int row,
2407 |                                             char *txt,
2408 |                                             int  len )
2409 |
2410 |   Description   :  Insert the 'len' characters beginning at pointer 'txt'
2411 |                    into the 'row' of the 'form'. The insertion occurs
2412 |                    on the beginning of the row, all other characters are
2413 |                    moved to the right. After the text a pad character will
2414 |                    be inserted to separate the text from the rest. If
2415 |                    necessary the insertion moves characters on the next
2416 |                    line to make place for the requested insertion string.
2417 |
2418 |   Return Values :  E_OK              - success
2419 |                    E_REQUEST_DENIED  -
2420 |                    E_SYSTEM_ERROR    - system error
2421 +--------------------------------------------------------------------------*/
2422 static int
Insert_String(FORM * form,int row,const FIELD_CELL * txt,int len)2423 Insert_String(FORM *form, int row, const FIELD_CELL *txt, int len)
2424 {
2425   FIELD *field = form->current;
2426   FIELD_CELL *bp = Address_Of_Row_In_Buffer(field, row);
2427   int datalen = (int)(After_End_Of_Data(bp, field->dcols) - bp);
2428   int freelen = field->dcols - datalen;
2429   int requiredlen = len + 1;
2430   int result = E_REQUEST_DENIED;
2431 
2432   if (freelen >= requiredlen)
2433     {
2434       wmove(form->w, row, 0);
2435       myINSNSTR(form->w, txt, len);
2436       wmove(form->w, row, len);
2437       myINSNSTR(form->w, &myBLANK, 1);
2438       result = E_OK;
2439     }
2440   else
2441     {
2442       /* we have to move characters on the next line. If we are on the
2443          last line this may work, if the field is growable */
2444       if ((row == (field->drows - 1)) && Growable(field))
2445 	{
2446 	  if (!Field_Grown(field, 1))
2447 	    return (E_SYSTEM_ERROR);
2448 	  /* !!!Side-Effect : might be changed due to growth!!! */
2449 	  bp = Address_Of_Row_In_Buffer(field, row);
2450 	}
2451 
2452       if (row < (field->drows - 1))
2453 	{
2454 	  const FIELD_CELL *split;
2455 
2456 	  split =
2457 	    After_Last_Whitespace_Character(bp,
2458 					    (int)(Get_Start_Of_Data(bp
2459 								    + field->dcols
2460 								    - requiredlen,
2461 								    requiredlen)
2462 						  - bp));
2463 	  /* split points now to the first character of the portion of the
2464 	     line that must be moved to the next line */
2465 	  datalen = (int)(split - bp);	/* + freelen has to stay on this line   */
2466 	  freelen = field->dcols - (datalen + freelen);		/* for the next line */
2467 
2468 	  if ((result = Insert_String(form, row + 1, split, freelen)) == E_OK)
2469 	    {
2470 	      wmove(form->w, row, datalen);
2471 	      wclrtoeol(form->w);
2472 	      wmove(form->w, row, 0);
2473 	      myINSNSTR(form->w, txt, len);
2474 	      wmove(form->w, row, len);
2475 	      myINSNSTR(form->w, &myBLANK, 1);
2476 	      return E_OK;
2477 	    }
2478 	}
2479     }
2480   return (result);
2481 }
2482 
2483 /*---------------------------------------------------------------------------
2484 |   Facility      :  libnform
2485 |   Function      :  static int Wrapping_Not_Necessary_Or_Wrapping_Ok(
2486 |                                             FORM * form)
2487 |
2488 |   Description   :  If a character has been entered into a field, it may
2489 |                    be that wrapping has to occur. This routine checks
2490 |                    whether or not wrapping is required and if so, performs
2491 |                    the wrapping.
2492 |
2493 |   Return Values :  E_OK              - no wrapping required or wrapping
2494 |                                        was successful
2495 |                    E_REQUEST_DENIED  -
2496 |                    E_SYSTEM_ERROR    - some system error
2497 +--------------------------------------------------------------------------*/
2498 static int
Wrapping_Not_Necessary_Or_Wrapping_Ok(FORM * form)2499 Wrapping_Not_Necessary_Or_Wrapping_Ok(FORM *form)
2500 {
2501   FIELD *field = form->current;
2502   int result = E_REQUEST_DENIED;
2503   bool Last_Row = ((field->drows - 1) == form->currow);
2504 
2505   if ((Field_Has_Option(field, O_WRAP)) &&	/* wrapping wanted     */
2506       (!Single_Line_Field(field)) &&	/* must be multi-line  */
2507       (There_Is_No_Room_For_A_Char_In_Line(form)) &&	/* line is full        */
2508       (!Last_Row || Growable(field)))	/* there are more lines */
2509     {
2510       FIELD_CELL *bp;
2511       const FIELD_CELL *split;
2512       int chars_to_be_wrapped;
2513       int chars_to_remain_on_line;
2514 
2515       if (Last_Row)
2516 	{
2517 	  /* the above logic already ensures, that in this case the field
2518 	     is growable */
2519 	  if (!Field_Grown(field, 1))
2520 	    return E_SYSTEM_ERROR;
2521 	}
2522       bp = Address_Of_Current_Row_In_Buffer(form);
2523       Window_To_Buffer(form, field);
2524       split = After_Last_Whitespace_Character(bp, field->dcols);
2525       /* split points to the first character of the sequence to be brought
2526          on the next line */
2527       chars_to_remain_on_line = (int)(split - bp);
2528       chars_to_be_wrapped = field->dcols - chars_to_remain_on_line;
2529       if (chars_to_remain_on_line > 0)
2530 	{
2531 	  if ((result = Insert_String(form, form->currow + 1, split,
2532 				      chars_to_be_wrapped)) == E_OK)
2533 	    {
2534 	      wmove(form->w, form->currow, chars_to_remain_on_line);
2535 	      wclrtoeol(form->w);
2536 	      if (form->curcol >= chars_to_remain_on_line)
2537 		{
2538 		  form->currow++;
2539 		  form->curcol -= chars_to_remain_on_line;
2540 		}
2541 	      return E_OK;
2542 	    }
2543 	}
2544       else
2545 	return E_OK;
2546       if (result != E_OK)
2547 	{
2548 	  DeleteChar(form);
2549 	  Window_To_Buffer(form, field);
2550 	  result = E_REQUEST_DENIED;
2551 	}
2552     }
2553   else
2554     result = E_OK;		/* wrapping was not necessary */
2555   return (result);
2556 }
2557 
2558 /*----------------------------------------------------------------------------
2559   Field Editing routines
2560   --------------------------------------------------------------------------*/
2561 
2562 /*---------------------------------------------------------------------------
2563 |   Facility      :  libnform
2564 |   Function      :  static int Field_Editing(
2565 |                                    int (* const fct) (FORM *),
2566 |                                    FORM * form)
2567 |
2568 |   Description   :  Generic routine for field editing requests. The driver
2569 |                    routines are only called for editable fields, the
2570 |                    _WINDOW_MODIFIED flag is set if editing occurred.
2571 |                    This is somewhat special due to the overload semantics
2572 |                    of the NEW_LINE and DEL_PREV requests.
2573 |
2574 |   Return Values :  Error code from low level drivers.
2575 +--------------------------------------------------------------------------*/
2576 static int
Field_Editing(int (* const fct)(FORM *),FORM * form)2577 Field_Editing(int (*const fct) (FORM *), FORM *form)
2578 {
2579   int res = E_REQUEST_DENIED;
2580 
2581   /* We have to deal here with the specific case of the overloaded
2582      behavior of New_Line and Delete_Previous requests.
2583      They may end up in navigational requests if we are on the first
2584      character in a field. But navigation is also allowed on non-
2585      editable fields.
2586    */
2587   if ((fct == FE_Delete_Previous) &&
2588       ((unsigned)form->opts & O_BS_OVERLOAD) &&
2589       First_Position_In_Current_Field(form))
2590     {
2591       res = Inter_Field_Navigation(FN_Previous_Field, form);
2592     }
2593   else
2594     {
2595       if (fct == FE_New_Line)
2596 	{
2597 	  if (((unsigned)form->opts & O_NL_OVERLOAD) &&
2598 	      First_Position_In_Current_Field(form))
2599 	    {
2600 	      res = Inter_Field_Navigation(FN_Next_Field, form);
2601 	    }
2602 	  else
2603 	    /* FE_New_Line deals itself with the _WINDOW_MODIFIED flag */
2604 	    res = fct(form);
2605 	}
2606       else
2607 	{
2608 	  /* From now on, everything must be editable */
2609 	  if ((unsigned)form->current->opts & O_EDIT)
2610 	    {
2611 	      res = fct(form);
2612 	      if (res == E_OK)
2613 		SetStatus(form, _WINDOW_MODIFIED);
2614 	    }
2615 	}
2616     }
2617   return res;
2618 }
2619 
2620 /*---------------------------------------------------------------------------
2621 |   Facility      :  libnform
2622 |   Function      :  static int FE_New_Line(FORM * form)
2623 |
2624 |   Description   :  Perform a new line request. This is rather complex
2625 |                    compared to other routines in this code due to the
2626 |                    rather difficult to understand description in the
2627 |                    manuals.
2628 |
2629 |   Return Values :  E_OK               - success
2630 |                    E_REQUEST_DENIED   - new line not allowed
2631 |                    E_SYSTEM_ERROR     - system error
2632 +--------------------------------------------------------------------------*/
2633 static int
FE_New_Line(FORM * form)2634 FE_New_Line(FORM *form)
2635 {
2636   FIELD *field = form->current;
2637   FIELD_CELL *bp;
2638   bool Last_Row = ((field->drows - 1) == form->currow);
2639 
2640   T((T_CALLED("FE_New_Line(%p)"), (void *)form));
2641   if (form->status & _OVLMODE)
2642     {
2643       if (Last_Row &&
2644 	  (!(Growable(field) && !Single_Line_Field(field))))
2645 	{
2646 	  if (!((unsigned)form->opts & O_NL_OVERLOAD))
2647 	    returnCode(E_REQUEST_DENIED);
2648 	  wmove(form->w, form->currow, form->curcol);
2649 	  wclrtoeol(form->w);
2650 	  /* we have to set this here, although it is also
2651 	     handled in the generic routine. The reason is,
2652 	     that FN_Next_Field may fail, but the form is
2653 	     definitively changed */
2654 	  SetStatus(form, _WINDOW_MODIFIED);
2655 	  returnCode(Inter_Field_Navigation(FN_Next_Field, form));
2656 	}
2657       else
2658 	{
2659 	  if (Last_Row && !Field_Grown(field, 1))
2660 	    {
2661 	      /* N.B.: due to the logic in the 'if', LastRow==TRUE
2662 	         means here that the field is growable and not
2663 	         a single-line field */
2664 	      returnCode(E_SYSTEM_ERROR);
2665 	    }
2666 	  wmove(form->w, form->currow, form->curcol);
2667 	  wclrtoeol(form->w);
2668 	  form->currow++;
2669 	  form->curcol = 0;
2670 	  SetStatus(form, _WINDOW_MODIFIED);
2671 	  returnCode(E_OK);
2672 	}
2673     }
2674   else
2675     {
2676       /* Insert Mode */
2677       if (Last_Row &&
2678 	  !(Growable(field) && !Single_Line_Field(field)))
2679 	{
2680 	  if (!((unsigned)form->opts & O_NL_OVERLOAD))
2681 	    returnCode(E_REQUEST_DENIED);
2682 	  returnCode(Inter_Field_Navigation(FN_Next_Field, form));
2683 	}
2684       else
2685 	{
2686 	  const FIELD_CELL *t;
2687 	  bool May_Do_It = !Last_Row && Is_There_Room_For_A_Line(form);
2688 
2689 	  if (!(May_Do_It || Growable(field)))
2690 	    returnCode(E_REQUEST_DENIED);
2691 	  if (!May_Do_It && !Field_Grown(field, 1))
2692 	    returnCode(E_SYSTEM_ERROR);
2693 
2694 	  bp = Address_Of_Current_Position_In_Buffer(form);
2695 	  t = After_End_Of_Data(bp, field->dcols - form->curcol);
2696 	  wmove(form->w, form->currow, form->curcol);
2697 	  wclrtoeol(form->w);
2698 	  form->currow++;
2699 	  form->curcol = 0;
2700 	  wmove(form->w, form->currow, form->curcol);
2701 	  winsertln(form->w);
2702 	  myADDNSTR(form->w, bp, (int)(t - bp));
2703 	  SetStatus(form, _WINDOW_MODIFIED);
2704 	  returnCode(E_OK);
2705 	}
2706     }
2707 }
2708 
2709 /*---------------------------------------------------------------------------
2710 |   Facility      :  libnform
2711 |   Function      :  static int FE_Insert_Character(FORM * form)
2712 |
2713 |   Description   :  Insert blank character at the cursor position
2714 |
2715 |   Return Values :  E_OK
2716 |                    E_REQUEST_DENIED
2717 +--------------------------------------------------------------------------*/
2718 static int
FE_Insert_Character(FORM * form)2719 FE_Insert_Character(FORM *form)
2720 {
2721   FIELD *field = form->current;
2722   int result = E_REQUEST_DENIED;
2723 
2724   T((T_CALLED("FE_Insert_Character(%p)"), (void *)form));
2725   if (Check_Char(form, field, field->type, (int)C_BLANK,
2726 		 (TypeArgument *)(field->arg)))
2727     {
2728       bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
2729 
2730       if (There_Is_Room ||
2731 	  ((Single_Line_Field(field) && Growable(field))))
2732 	{
2733 	  if (!There_Is_Room && !Field_Grown(field, 1))
2734 	    result = E_SYSTEM_ERROR;
2735 	  else
2736 	    {
2737 	      winsch(form->w, (chtype)C_BLANK);
2738 	      result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form);
2739 	    }
2740 	}
2741     }
2742   returnCode(result);
2743 }
2744 
2745 /*---------------------------------------------------------------------------
2746 |   Facility      :  libnform
2747 |   Function      :  static int FE_Insert_Line(FORM * form)
2748 |
2749 |   Description   :  Insert a blank line at the cursor position
2750 |
2751 |   Return Values :  E_OK               - success
2752 |                    E_REQUEST_DENIED   - line can not be inserted
2753 +--------------------------------------------------------------------------*/
2754 static int
FE_Insert_Line(FORM * form)2755 FE_Insert_Line(FORM *form)
2756 {
2757   FIELD *field = form->current;
2758   int result = E_REQUEST_DENIED;
2759 
2760   T((T_CALLED("FE_Insert_Line(%p)"), (void *)form));
2761   if (Check_Char(form, field,
2762 		 field->type, (int)C_BLANK, (TypeArgument *)(field->arg)))
2763     {
2764       bool Maybe_Done = (form->currow != (field->drows - 1)) &&
2765       Is_There_Room_For_A_Line(form);
2766 
2767       if (!Single_Line_Field(field) &&
2768 	  (Maybe_Done || Growable(field)))
2769 	{
2770 	  if (!Maybe_Done && !Field_Grown(field, 1))
2771 	    result = E_SYSTEM_ERROR;
2772 	  else
2773 	    {
2774 	      form->curcol = 0;
2775 	      winsertln(form->w);
2776 	      result = E_OK;
2777 	    }
2778 	}
2779     }
2780   returnCode(result);
2781 }
2782 
2783 /*---------------------------------------------------------------------------
2784 |   Facility      :  libnform
2785 |   Function      :  static int FE_Delete_Character(FORM * form)
2786 |
2787 |   Description   :  Delete character at the cursor position
2788 |
2789 |   Return Values :  E_OK    - success
2790 +--------------------------------------------------------------------------*/
2791 static int
FE_Delete_Character(FORM * form)2792 FE_Delete_Character(FORM *form)
2793 {
2794   T((T_CALLED("FE_Delete_Character(%p)"), (void *)form));
2795   DeleteChar(form);
2796   returnCode(E_OK);
2797 }
2798 
2799 /*---------------------------------------------------------------------------
2800 |   Facility      :  libnform
2801 |   Function      :  static int FE_Delete_Previous(FORM * form)
2802 |
2803 |   Description   :  Delete character before cursor. Again this is a rather
2804 |                    difficult piece compared to others due to the overloading
2805 |                    semantics of backspace.
2806 |                    N.B.: The case of overloaded BS on first field position
2807 |                          is already handled in the generic routine.
2808 |
2809 |   Return Values :  E_OK                - success
2810 |                    E_REQUEST_DENIED    - Character can't be deleted
2811 +--------------------------------------------------------------------------*/
2812 static int
FE_Delete_Previous(FORM * form)2813 FE_Delete_Previous(FORM *form)
2814 {
2815   const FIELD *field = form->current;
2816 
2817   T((T_CALLED("FE_Delete_Previous(%p)"), (void *)form));
2818   if (First_Position_In_Current_Field(form))
2819     returnCode(E_REQUEST_DENIED);
2820 
2821   if ((--(form->curcol)) < 0)
2822     {
2823       FIELD_CELL *this_line, *prev_line;
2824       const FIELD_CELL *prev_end, *this_end;
2825       int this_row = form->currow;
2826 
2827       form->curcol++;
2828       if (form->status & _OVLMODE)
2829 	returnCode(E_REQUEST_DENIED);
2830 
2831       prev_line = Address_Of_Row_In_Buffer(field, (form->currow - 1));
2832       this_line = Address_Of_Row_In_Buffer(field, (form->currow));
2833       Synchronize_Buffer(form);
2834       prev_end = After_End_Of_Data(prev_line, field->dcols);
2835       this_end = After_End_Of_Data(this_line, field->dcols);
2836       if ((int)(this_end - this_line) >
2837 	  (field->cols - (int)(prev_end - prev_line)))
2838 	returnCode(E_REQUEST_DENIED);
2839       wmove(form->w, form->currow, form->curcol);
2840       wdeleteln(form->w);
2841       Adjust_Cursor_Position(form, prev_end);
2842       /*
2843        * If we did not really move to the previous line, help the user a
2844        * little.  It is however a little inconsistent.  Normally, when
2845        * backspacing around the point where text wraps to a new line in a
2846        * multi-line form, we absorb one keystroke for the wrapping point.  That
2847        * is consistent with SVr4 forms.  However, SVr4 does not allow typing
2848        * into the last column of the field, and requires the user to enter a
2849        * newline to move to the next line.  Therefore it can consistently eat
2850        * that keystroke.  Since ncurses allows the last column, it wraps
2851        * automatically (given the proper options).  But we cannot eat the
2852        * keystroke to back over the wrapping point, since that would put the
2853        * cursor past the end of the form field.  In this case, just delete the
2854        * character at the end of the field.
2855        */
2856       if (form->currow == this_row && this_row > 0)
2857 	{
2858 	  form->currow -= 1;
2859 	  form->curcol = field->dcols - 1;
2860 	  DeleteChar(form);
2861 	}
2862       else
2863 	{
2864 	  wmove(form->w, form->currow, form->curcol);
2865 	  myADDNSTR(form->w, this_line, (int)(this_end - this_line));
2866 	}
2867     }
2868   else
2869     {
2870       DeleteChar(form);
2871     }
2872   returnCode(E_OK);
2873 }
2874 
2875 /*---------------------------------------------------------------------------
2876 |   Facility      :  libnform
2877 |   Function      :  static int FE_Delete_Line(FORM * form)
2878 |
2879 |   Description   :  Delete line at cursor position.
2880 |
2881 |   Return Values :  E_OK  - success
2882 +--------------------------------------------------------------------------*/
2883 static int
FE_Delete_Line(FORM * form)2884 FE_Delete_Line(FORM *form)
2885 {
2886   T((T_CALLED("FE_Delete_Line(%p)"), (void *)form));
2887   form->curcol = 0;
2888   wdeleteln(form->w);
2889   returnCode(E_OK);
2890 }
2891 
2892 /*---------------------------------------------------------------------------
2893 |   Facility      :  libnform
2894 |   Function      :  static int FE_Delete_Word(FORM * form)
2895 |
2896 |   Description   :  Delete word at cursor position
2897 |
2898 |   Return Values :  E_OK               - success
2899 |                    E_REQUEST_DENIED   - failure
2900 +--------------------------------------------------------------------------*/
2901 static int
FE_Delete_Word(FORM * form)2902 FE_Delete_Word(FORM *form)
2903 {
2904   const FIELD *field = form->current;
2905   FIELD_CELL *bp = Address_Of_Current_Row_In_Buffer(form);
2906   const FIELD_CELL *ep = bp + field->dcols;
2907   FIELD_CELL *cp = bp + form->curcol;
2908   FIELD_CELL *s;
2909 
2910   T((T_CALLED("FE_Delete_Word(%p)"), (void *)form));
2911   Synchronize_Buffer(form);
2912   if (ISBLANK(*cp))
2913     returnCode(E_REQUEST_DENIED);	/* not in word */
2914 
2915   /* move cursor to begin of word and erase to end of screen-line */
2916   Adjust_Cursor_Position(form,
2917 			 After_Last_Whitespace_Character(bp, form->curcol));
2918   wmove(form->w, form->currow, form->curcol);
2919   wclrtoeol(form->w);
2920 
2921   /* skip over word in buffer */
2922   s = Get_First_Whitespace_Character(cp, (int)(ep - cp));
2923   /* to begin of next word    */
2924   s = Get_Start_Of_Data(s, (int)(ep - s));
2925   if ((s != cp) && !ISBLANK(*s))
2926     {
2927       /* copy remaining line to window */
2928       myADDNSTR(form->w, s, (int)(s - After_End_Of_Data(s, (int)(ep - s))));
2929     }
2930   returnCode(E_OK);
2931 }
2932 
2933 /*---------------------------------------------------------------------------
2934 |   Facility      :  libnform
2935 |   Function      :  static int FE_Clear_To_End_Of_Line(FORM * form)
2936 |
2937 |   Description   :  Clear to end of current line.
2938 |
2939 |   Return Values :  E_OK   - success
2940 +--------------------------------------------------------------------------*/
2941 static int
FE_Clear_To_End_Of_Line(FORM * form)2942 FE_Clear_To_End_Of_Line(FORM *form)
2943 {
2944   T((T_CALLED("FE_Clear_To_End_Of_Line(%p)"), (void *)form));
2945   wmove(form->w, form->currow, form->curcol);
2946   wclrtoeol(form->w);
2947   returnCode(E_OK);
2948 }
2949 
2950 /*---------------------------------------------------------------------------
2951 |   Facility      :  libnform
2952 |   Function      :  static int FE_Clear_To_End_Of_Field(FORM * form)
2953 |
2954 |   Description   :  Clear to end of field.
2955 |
2956 |   Return Values :  E_OK   - success
2957 +--------------------------------------------------------------------------*/
2958 static int
FE_Clear_To_End_Of_Field(FORM * form)2959 FE_Clear_To_End_Of_Field(FORM *form)
2960 {
2961   T((T_CALLED("FE_Clear_To_End_Of_Field(%p)"), (void *)form));
2962   wmove(form->w, form->currow, form->curcol);
2963   wclrtobot(form->w);
2964   returnCode(E_OK);
2965 }
2966 
2967 /*---------------------------------------------------------------------------
2968 |   Facility      :  libnform
2969 |   Function      :  static int FE_Clear_Field(FORM * form)
2970 |
2971 |   Description   :  Clear entire field.
2972 |
2973 |   Return Values :  E_OK   - success
2974 +--------------------------------------------------------------------------*/
2975 static int
FE_Clear_Field(FORM * form)2976 FE_Clear_Field(FORM *form)
2977 {
2978   T((T_CALLED("FE_Clear_Field(%p)"), (void *)form));
2979   form->currow = form->curcol = 0;
2980   werase(form->w);
2981   returnCode(E_OK);
2982 }
2983 /*----------------------------------------------------------------------------
2984   END of Field Editing routines
2985   --------------------------------------------------------------------------*/
2986 
2987 /*----------------------------------------------------------------------------
2988   Edit Mode routines
2989   --------------------------------------------------------------------------*/
2990 
2991 /*---------------------------------------------------------------------------
2992 |   Facility      :  libnform
2993 |   Function      :  static int EM_Overlay_Mode(FORM * form)
2994 |
2995 |   Description   :  Switch to overlay mode.
2996 |
2997 |   Return Values :  E_OK   - success
2998 +--------------------------------------------------------------------------*/
2999 static int
EM_Overlay_Mode(FORM * form)3000 EM_Overlay_Mode(FORM *form)
3001 {
3002   T((T_CALLED("EM_Overlay_Mode(%p)"), (void *)form));
3003   SetStatus(form, _OVLMODE);
3004   returnCode(E_OK);
3005 }
3006 
3007 /*---------------------------------------------------------------------------
3008 |   Facility      :  libnform
3009 |   Function      :  static int EM_Insert_Mode(FORM * form)
3010 |
3011 |   Description   :  Switch to insert mode
3012 |
3013 |   Return Values :  E_OK   - success
3014 +--------------------------------------------------------------------------*/
3015 static int
EM_Insert_Mode(FORM * form)3016 EM_Insert_Mode(FORM *form)
3017 {
3018   T((T_CALLED("EM_Insert_Mode(%p)"), (void *)form));
3019   ClrStatus(form, _OVLMODE);
3020   returnCode(E_OK);
3021 }
3022 
3023 /*----------------------------------------------------------------------------
3024   END of Edit Mode routines
3025   --------------------------------------------------------------------------*/
3026 
3027 /*----------------------------------------------------------------------------
3028   Helper routines for Choice Requests
3029   --------------------------------------------------------------------------*/
3030 
3031 /*---------------------------------------------------------------------------
3032 |   Facility      :  libnform
3033 |   Function      :  static bool Next_Choice(FORM * form,
3034 |                                            FIELDTYPE * typ,
3035 |                                            FIELD * field,
3036 |                                            TypeArgument *argp)
3037 |
3038 |   Description   :  Get the next field choice. For linked types this is
3039 |                    done recursively.
3040 |
3041 |   Return Values :  TRUE    - next choice successfully retrieved
3042 |                    FALSE   - couldn't retrieve next choice
3043 +--------------------------------------------------------------------------*/
3044 static bool
Next_Choice(FORM * form,FIELDTYPE * typ,FIELD * field,TypeArgument * argp)3045 Next_Choice(FORM *form, FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
3046 {
3047   if (!typ || !(typ->status & _HAS_CHOICE))
3048     return FALSE;
3049 
3050   if (typ->status & _LINKED_TYPE)
3051     {
3052       assert(argp);
3053       return (
3054 	       Next_Choice(form, typ->left, field, argp->left) ||
3055 	       Next_Choice(form, typ->right, field, argp->right));
3056     }
3057   else
3058     {
3059 #if NCURSES_INTEROP_FUNCS
3060       assert(typ->enum_next.onext);
3061       if (typ->status & _GENERIC)
3062 	return typ->enum_next.gnext(form, field, (void *)argp);
3063       else
3064 	return typ->enum_next.onext(field, (void *)argp);
3065 #else
3066       assert(typ->next);
3067       return typ->next(field, (void *)argp);
3068 #endif
3069     }
3070 }
3071 
3072 /*---------------------------------------------------------------------------
3073 |   Facility      :  libnform
3074 |   Function      :  static bool Previous_Choice(FORM * form,
3075 |                                                FIELDTYPE * typ,
3076 |                                                FIELD * field,
3077 |                                                TypeArgument *argp)
3078 |
3079 |   Description   :  Get the previous field choice. For linked types this
3080 |                    is done recursively.
3081 |
3082 |   Return Values :  TRUE    - previous choice successfully retrieved
3083 |                    FALSE   - couldn't retrieve previous choice
3084 +--------------------------------------------------------------------------*/
3085 static bool
Previous_Choice(FORM * form,FIELDTYPE * typ,FIELD * field,TypeArgument * argp)3086 Previous_Choice(FORM *form, FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
3087 {
3088   if (!typ || !(typ->status & _HAS_CHOICE))
3089     return FALSE;
3090 
3091   if (typ->status & _LINKED_TYPE)
3092     {
3093       assert(argp);
3094       return (
3095 	       Previous_Choice(form, typ->left, field, argp->left) ||
3096 	       Previous_Choice(form, typ->right, field, argp->right));
3097     }
3098   else
3099     {
3100 #if NCURSES_INTEROP_FUNCS
3101       assert(typ->enum_prev.oprev);
3102       if (typ->status & _GENERIC)
3103 	return typ->enum_prev.gprev(form, field, (void *)argp);
3104       else
3105 	return typ->enum_prev.oprev(field, (void *)argp);
3106 #else
3107       assert(typ->prev);
3108       return typ->prev(field, (void *)argp);
3109 #endif
3110     }
3111 }
3112 /*----------------------------------------------------------------------------
3113   End of Helper routines for Choice Requests
3114   --------------------------------------------------------------------------*/
3115 
3116 /*----------------------------------------------------------------------------
3117   Routines for Choice Requests
3118   --------------------------------------------------------------------------*/
3119 
3120 /*---------------------------------------------------------------------------
3121 |   Facility      :  libnform
3122 |   Function      :  static int CR_Next_Choice(FORM * form)
3123 |
3124 |   Description   :  Get the next field choice.
3125 |
3126 |   Return Values :  E_OK              - success
3127 |                    E_REQUEST_DENIED  - next choice couldn't be retrieved
3128 +--------------------------------------------------------------------------*/
3129 static int
CR_Next_Choice(FORM * form)3130 CR_Next_Choice(FORM *form)
3131 {
3132   FIELD *field = form->current;
3133 
3134   T((T_CALLED("CR_Next_Choice(%p)"), (void *)form));
3135   Synchronize_Buffer(form);
3136   returnCode((Next_Choice(form, field->type, field, (TypeArgument *)(field->arg)))
3137 	     ? E_OK
3138 	     : E_REQUEST_DENIED);
3139 }
3140 
3141 /*---------------------------------------------------------------------------
3142 |   Facility      :  libnform
3143 |   Function      :  static int CR_Previous_Choice(FORM * form)
3144 |
3145 |   Description   :  Get the previous field choice.
3146 |
3147 |   Return Values :  E_OK              - success
3148 |                    E_REQUEST_DENIED  - prev. choice couldn't be retrieved
3149 +--------------------------------------------------------------------------*/
3150 static int
CR_Previous_Choice(FORM * form)3151 CR_Previous_Choice(FORM *form)
3152 {
3153   FIELD *field = form->current;
3154 
3155   T((T_CALLED("CR_Previous_Choice(%p)"), (void *)form));
3156   Synchronize_Buffer(form);
3157   returnCode((Previous_Choice(form, field->type, field, (TypeArgument *)(field->arg)))
3158 	     ? E_OK
3159 	     : E_REQUEST_DENIED);
3160 }
3161 /*----------------------------------------------------------------------------
3162   End of Routines for Choice Requests
3163   --------------------------------------------------------------------------*/
3164 
3165 /*----------------------------------------------------------------------------
3166   Helper routines for Field Validations.
3167   --------------------------------------------------------------------------*/
3168 
3169 /*---------------------------------------------------------------------------
3170 |   Facility      :  libnform
3171 |   Function      :  static bool Check_Field(FORM* form,
3172 |                                            FIELDTYPE * typ,
3173 |                                            FIELD * field,
3174 |                                            TypeArgument * argp)
3175 |
3176 |   Description   :  Check the field according to its fieldtype and its
3177 |                    actual arguments. For linked fieldtypes this is done
3178 |                    recursively.
3179 |
3180 |   Return Values :  TRUE       - field is valid
3181 |                    FALSE      - field is invalid.
3182 +--------------------------------------------------------------------------*/
3183 static bool
Check_Field(FORM * form,FIELDTYPE * typ,FIELD * field,TypeArgument * argp)3184 Check_Field(FORM *form, FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
3185 {
3186   if (typ)
3187     {
3188       if (Field_Has_Option(field, O_NULLOK))
3189 	{
3190 	  FIELD_CELL *bp = field->buf;
3191 
3192 	  assert(bp);
3193 	  while (ISBLANK(*bp))
3194 	    {
3195 	      bp++;
3196 	    }
3197 	  if (CharOf(*bp) == 0)
3198 	    return TRUE;
3199 	}
3200 
3201       if (typ->status & _LINKED_TYPE)
3202 	{
3203 	  assert(argp);
3204 	  return (
3205 		   Check_Field(form, typ->left, field, argp->left) ||
3206 		   Check_Field(form, typ->right, field, argp->right));
3207 	}
3208       else
3209 	{
3210 #if NCURSES_INTEROP_FUNCS
3211 	  if (typ->fieldcheck.ofcheck)
3212 	    {
3213 	      if (typ->status & _GENERIC)
3214 		return typ->fieldcheck.gfcheck(form, field, (void *)argp);
3215 	      else
3216 		return typ->fieldcheck.ofcheck(field, (void *)argp);
3217 	    }
3218 #else
3219 	  if (typ->fcheck)
3220 	    return typ->fcheck(field, (void *)argp);
3221 #endif
3222 	}
3223     }
3224   return TRUE;
3225 }
3226 
3227 /*---------------------------------------------------------------------------
3228 |   Facility      :  libnform
3229 |   Function      :  bool _nc_Internal_Validation(FORM * form )
3230 |
3231 |   Description   :  Validate the current field of the form.
3232 |
3233 |   Return Values :  TRUE  - field is valid
3234 |                    FALSE - field is invalid
3235 +--------------------------------------------------------------------------*/
3236 FORM_EXPORT(bool)
_nc_Internal_Validation(FORM * form)3237 _nc_Internal_Validation(FORM *form)
3238 {
3239   FIELD *field;
3240 
3241   field = form->current;
3242 
3243   Synchronize_Buffer(form);
3244   if ((form->status & _FCHECK_REQUIRED) ||
3245       (!(Field_Has_Option(field, O_PASSOK))))
3246     {
3247       if (!Check_Field(form, field->type, field, (TypeArgument *)(field->arg)))
3248 	return FALSE;
3249       ClrStatus(form, _FCHECK_REQUIRED);
3250       SetStatus(field, _CHANGED);
3251       Synchronize_Linked_Fields(field);
3252     }
3253   return TRUE;
3254 }
3255 /*----------------------------------------------------------------------------
3256   End of Helper routines for Field Validations.
3257   --------------------------------------------------------------------------*/
3258 
3259 /*----------------------------------------------------------------------------
3260   Routines for Field Validation.
3261   --------------------------------------------------------------------------*/
3262 
3263 /*---------------------------------------------------------------------------
3264 |   Facility      :  libnform
3265 |   Function      :  static int FV_Validation(FORM * form)
3266 |
3267 |   Description   :  Validate the current field of the form.
3268 |
3269 |   Return Values :  E_OK             - field valid
3270 |                    E_INVALID_FIELD  - field not valid
3271 +--------------------------------------------------------------------------*/
3272 static int
FV_Validation(FORM * form)3273 FV_Validation(FORM *form)
3274 {
3275   T((T_CALLED("FV_Validation(%p)"), (void *)form));
3276   if (_nc_Internal_Validation(form))
3277     returnCode(E_OK);
3278   else
3279     returnCode(E_INVALID_FIELD);
3280 }
3281 /*----------------------------------------------------------------------------
3282   End of routines for Field Validation.
3283   --------------------------------------------------------------------------*/
3284 
3285 /*----------------------------------------------------------------------------
3286   Helper routines for Inter-Field Navigation
3287   --------------------------------------------------------------------------*/
3288 
3289 /*---------------------------------------------------------------------------
3290 |   Facility      :  libnform
3291 |   Function      :  static FIELD *Next_Field_On_Page(FIELD * field)
3292 |
3293 |   Description   :  Get the next field after the given field on the current
3294 |                    page. The order of fields is the one defined by the
3295 |                    field's array. Only visible and active fields are
3296 |                    counted.
3297 |
3298 |   Return Values :  Pointer to the next field.
3299 +--------------------------------------------------------------------------*/
3300 NCURSES_INLINE static FIELD *
Next_Field_On_Page(FIELD * field)3301 Next_Field_On_Page(FIELD *field)
3302 {
3303   FORM *form = field->form;
3304   FIELD **field_on_page = &form->field[field->index];
3305   FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
3306   FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3307 
3308   do
3309     {
3310       field_on_page =
3311 	(field_on_page == last_on_page) ? first_on_page : field_on_page + 1;
3312       if (Field_Is_Selectable(*field_on_page))
3313 	break;
3314     }
3315   while (field != (*field_on_page));
3316   return (*field_on_page);
3317 }
3318 
3319 /*---------------------------------------------------------------------------
3320 |   Facility      :  libnform
3321 |   Function      :  FIELD* _nc_First_Active_Field(FORM * form)
3322 |
3323 |   Description   :  Get the first active field on the current page,
3324 |                    if there are such. If there are none, get the first
3325 |                    visible field on the page. If there are also none,
3326 |                    we return the first field on page and hope the best.
3327 |
3328 |   Return Values :  Pointer to calculated field.
3329 +--------------------------------------------------------------------------*/
3330 FORM_EXPORT(FIELD *)
_nc_First_Active_Field(FORM * form)3331 _nc_First_Active_Field(FORM *form)
3332 {
3333   FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3334   FIELD *proposed = Next_Field_On_Page(*last_on_page);
3335 
3336   if (proposed == *last_on_page)
3337     {
3338       /* there might be the special situation, where there is no
3339          active and visible field on the current page. We then select
3340          the first visible field on this readonly page
3341        */
3342       if (Field_Is_Not_Selectable(proposed))
3343 	{
3344 	  FIELD **field = &form->field[proposed->index];
3345 	  FIELD **first = &form->field[form->page[form->curpage].pmin];
3346 
3347 	  do
3348 	    {
3349 	      field = (field == last_on_page) ? first : field + 1;
3350 	      if (Field_Has_Option(*field, O_VISIBLE))
3351 		break;
3352 	    }
3353 	  while (proposed != (*field));
3354 
3355 	  proposed = *field;
3356 
3357 	  if ((proposed == *last_on_page) &&
3358 	      !((unsigned)proposed->opts & O_VISIBLE))
3359 	    {
3360 	      /* This means, there is also no visible field on the page.
3361 	         So we propose the first one and hope the very best...
3362 	         Some very clever user has designed a readonly and invisible
3363 	         page on this form.
3364 	       */
3365 	      proposed = *first;
3366 	    }
3367 	}
3368     }
3369   return (proposed);
3370 }
3371 
3372 /*---------------------------------------------------------------------------
3373 |   Facility      :  libnform
3374 |   Function      :  static FIELD *Previous_Field_On_Page(FIELD * field)
3375 |
3376 |   Description   :  Get the previous field before the given field on the
3377 |                    current page. The order of fields is the one defined by
3378 |                    the field's array. Only visible and active fields are
3379 |                    counted.
3380 |
3381 |   Return Values :  Pointer to the previous field.
3382 +--------------------------------------------------------------------------*/
3383 NCURSES_INLINE static FIELD *
Previous_Field_On_Page(FIELD * field)3384 Previous_Field_On_Page(FIELD *field)
3385 {
3386   FORM *form = field->form;
3387   FIELD **field_on_page = &form->field[field->index];
3388   FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
3389   FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3390 
3391   do
3392     {
3393       field_on_page =
3394 	(field_on_page == first_on_page) ? last_on_page : field_on_page - 1;
3395       if (Field_Is_Selectable(*field_on_page))
3396 	break;
3397     }
3398   while (field != (*field_on_page));
3399 
3400   return (*field_on_page);
3401 }
3402 
3403 /*---------------------------------------------------------------------------
3404 |   Facility      :  libnform
3405 |   Function      :  static FIELD *Sorted_Next_Field(FIELD * field)
3406 |
3407 |   Description   :  Get the next field after the given field on the current
3408 |                    page. The order of fields is the one defined by the
3409 |                    (row,column) geometry, rows are major.
3410 |
3411 |   Return Values :  Pointer to the next field.
3412 +--------------------------------------------------------------------------*/
3413 NCURSES_INLINE static FIELD *
Sorted_Next_Field(FIELD * field)3414 Sorted_Next_Field(FIELD *field)
3415 {
3416   FIELD *field_on_page = field;
3417 
3418   do
3419     {
3420       field_on_page = field_on_page->snext;
3421       if (Field_Is_Selectable(field_on_page))
3422 	break;
3423     }
3424   while (field_on_page != field);
3425 
3426   return (field_on_page);
3427 }
3428 
3429 /*---------------------------------------------------------------------------
3430 |   Facility      :  libnform
3431 |   Function      :  static FIELD *Sorted_Previous_Field(FIELD * field)
3432 |
3433 |   Description   :  Get the previous field before the given field on the
3434 |                    current page. The order of fields is the one defined
3435 |                    by the (row,column) geometry, rows are major.
3436 |
3437 |   Return Values :  Pointer to the previous field.
3438 +--------------------------------------------------------------------------*/
3439 NCURSES_INLINE static FIELD *
Sorted_Previous_Field(FIELD * field)3440 Sorted_Previous_Field(FIELD *field)
3441 {
3442   FIELD *field_on_page = field;
3443 
3444   do
3445     {
3446       field_on_page = field_on_page->sprev;
3447       if (Field_Is_Selectable(field_on_page))
3448 	break;
3449     }
3450   while (field_on_page != field);
3451 
3452   return (field_on_page);
3453 }
3454 
3455 /*---------------------------------------------------------------------------
3456 |   Facility      :  libnform
3457 |   Function      :  static FIELD *Left_Neighbor_Field(FIELD * field)
3458 |
3459 |   Description   :  Get the left neighbor of the field on the same line
3460 |                    and the same page. Cycles through the line.
3461 |
3462 |   Return Values :  Pointer to left neighbor field.
3463 +--------------------------------------------------------------------------*/
3464 NCURSES_INLINE static FIELD *
Left_Neighbor_Field(FIELD * field)3465 Left_Neighbor_Field(FIELD *field)
3466 {
3467   FIELD *field_on_page = field;
3468 
3469   /* For a field that has really a left neighbor, the while clause
3470      immediately fails and the loop is left, positioned at the right
3471      neighbor. Otherwise we cycle backwards through the sorted field list
3472      until we enter the same line (from the right end).
3473    */
3474   do
3475     {
3476       field_on_page = Sorted_Previous_Field(field_on_page);
3477     }
3478   while (field_on_page->frow != field->frow);
3479 
3480   return (field_on_page);
3481 }
3482 
3483 /*---------------------------------------------------------------------------
3484 |   Facility      :  libnform
3485 |   Function      :  static FIELD *Right_Neighbor_Field(FIELD * field)
3486 |
3487 |   Description   :  Get the right neighbor of the field on the same line
3488 |                    and the same page.
3489 |
3490 |   Return Values :  Pointer to right neighbor field.
3491 +--------------------------------------------------------------------------*/
3492 NCURSES_INLINE static FIELD *
Right_Neighbor_Field(FIELD * field)3493 Right_Neighbor_Field(FIELD *field)
3494 {
3495   FIELD *field_on_page = field;
3496 
3497   /* See the comments on Left_Neighbor_Field to understand how it works */
3498   do
3499     {
3500       field_on_page = Sorted_Next_Field(field_on_page);
3501     }
3502   while (field_on_page->frow != field->frow);
3503 
3504   return (field_on_page);
3505 }
3506 
3507 /*---------------------------------------------------------------------------
3508 |   Facility      :  libnform
3509 |   Function      :  static FIELD *Upper_Neighbor_Field(FIELD * field)
3510 |
3511 |   Description   :  Because of the row-major nature of sorting the fields,
3512 |                    it is more difficult to define what the upper neighbor
3513 |                    field really means. We define that it must be on a
3514 |                    'previous' line (cyclic order!) and is the rightmost
3515 |                    field lying on the left side of the given field. If
3516 |                    this set is empty, we take the first field on the line.
3517 |
3518 |   Return Values :  Pointer to the upper neighbor field.
3519 +--------------------------------------------------------------------------*/
3520 static FIELD *
Upper_Neighbor_Field(FIELD * field)3521 Upper_Neighbor_Field(FIELD *field)
3522 {
3523   FIELD *field_on_page = field;
3524   int frow = field->frow;
3525   int fcol = field->fcol;
3526 
3527   /* Walk back to the 'previous' line. The second term in the while clause
3528      just guarantees that we stop if we cycled through the line because
3529      there might be no 'previous' line if the page has just one line.
3530    */
3531   do
3532     {
3533       field_on_page = Sorted_Previous_Field(field_on_page);
3534     }
3535   while (field_on_page->frow == frow && field_on_page->fcol != fcol);
3536 
3537   if (field_on_page->frow != frow)
3538     {
3539       /* We really found a 'previous' line. We are positioned at the
3540          rightmost field on this line */
3541       frow = field_on_page->frow;
3542 
3543       /* We walk to the left as long as we are really right of the
3544          field. */
3545       while (field_on_page->frow == frow && field_on_page->fcol > fcol)
3546 	field_on_page = Sorted_Previous_Field(field_on_page);
3547 
3548       /* If we wrapped, just go to the right which is the first field on
3549          the row */
3550       if (field_on_page->frow != frow)
3551 	field_on_page = Sorted_Next_Field(field_on_page);
3552     }
3553 
3554   return (field_on_page);
3555 }
3556 
3557 /*---------------------------------------------------------------------------
3558 |   Facility      :  libnform
3559 |   Function      :  static FIELD *Down_Neighbor_Field(FIELD * field)
3560 |
3561 |   Description   :  Because of the row-major nature of sorting the fields,
3562 |                    it is more difficult to define what the down neighbor
3563 |                    field really means. We define that it must be on a
3564 |                    'next' line (cyclic order!) and is the leftmost
3565 |                    field laying on the right side of the given field. If
3566 |                    this set is empty, we take the last field on the line.
3567 |
3568 |   Return Values :  Pointer to the upper neighbor field.
3569 +--------------------------------------------------------------------------*/
3570 static FIELD *
Down_Neighbor_Field(FIELD * field)3571 Down_Neighbor_Field(FIELD *field)
3572 {
3573   FIELD *field_on_page = field;
3574   int frow = field->frow;
3575   int fcol = field->fcol;
3576 
3577   /* Walk forward to the 'next' line. The second term in the while clause
3578      just guarantees that we stop if we cycled through the line because
3579      there might be no 'next' line if the page has just one line.
3580    */
3581   do
3582     {
3583       field_on_page = Sorted_Next_Field(field_on_page);
3584     }
3585   while (field_on_page->frow == frow && field_on_page->fcol != fcol);
3586 
3587   if (field_on_page->frow != frow)
3588     {
3589       /* We really found a 'next' line. We are positioned at the rightmost
3590          field on this line */
3591       frow = field_on_page->frow;
3592 
3593       /* We walk to the right as long as we are really left of the
3594          field. */
3595       while (field_on_page->frow == frow && field_on_page->fcol < fcol)
3596 	field_on_page = Sorted_Next_Field(field_on_page);
3597 
3598       /* If we wrapped, just go to the left which is the last field on
3599          the row */
3600       if (field_on_page->frow != frow)
3601 	field_on_page = Sorted_Previous_Field(field_on_page);
3602     }
3603 
3604   return (field_on_page);
3605 }
3606 
3607 /*----------------------------------------------------------------------------
3608   Inter-Field Navigation routines
3609   --------------------------------------------------------------------------*/
3610 
3611 /*---------------------------------------------------------------------------
3612 |   Facility      :  libnform
3613 |   Function      :  static int Inter_Field_Navigation(
3614 |                                           int (* const fct) (FORM *),
3615 |                                           FORM * form)
3616 |
3617 |   Description   :  Generic behavior for changing the current field, the
3618 |                    field is left and a new field is entered. So the field
3619 |                    must be validated and the field init/term hooks must
3620 |                    be called.
3621 |
3622 |   Return Values :  E_OK                - success
3623 |                    E_INVALID_FIELD     - field is invalid
3624 |                    some other          - error from subordinate call
3625 +--------------------------------------------------------------------------*/
3626 static int
Inter_Field_Navigation(int (* const fct)(FORM *),FORM * form)3627 Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form)
3628 {
3629   int res;
3630 
3631   if (!_nc_Internal_Validation(form))
3632     res = E_INVALID_FIELD;
3633   else
3634     {
3635       Call_Hook(form, fieldterm);
3636       res = fct(form);
3637       Call_Hook(form, fieldinit);
3638     }
3639   return res;
3640 }
3641 
3642 /*---------------------------------------------------------------------------
3643 |   Facility      :  libnform
3644 |   Function      :  static int FN_Next_Field(FORM * form)
3645 |
3646 |   Description   :  Move to the next field on the current page of the form
3647 |
3648 |   Return Values :  E_OK                 - success
3649 |                    != E_OK              - error from subordinate call
3650 +--------------------------------------------------------------------------*/
3651 static int
FN_Next_Field(FORM * form)3652 FN_Next_Field(FORM *form)
3653 {
3654   T((T_CALLED("FN_Next_Field(%p)"), (void *)form));
3655   returnCode(_nc_Set_Current_Field(form,
3656 				   Next_Field_On_Page(form->current)));
3657 }
3658 
3659 /*---------------------------------------------------------------------------
3660 |   Facility      :  libnform
3661 |   Function      :  static int FN_Previous_Field(FORM * form)
3662 |
3663 |   Description   :  Move to the previous field on the current page of the
3664 |                    form
3665 |
3666 |   Return Values :  E_OK                 - success
3667 |                    != E_OK              - error from subordinate call
3668 +--------------------------------------------------------------------------*/
3669 static int
FN_Previous_Field(FORM * form)3670 FN_Previous_Field(FORM *form)
3671 {
3672   T((T_CALLED("FN_Previous_Field(%p)"), (void *)form));
3673   returnCode(_nc_Set_Current_Field(form,
3674 				   Previous_Field_On_Page(form->current)));
3675 }
3676 
3677 /*---------------------------------------------------------------------------
3678 |   Facility      :  libnform
3679 |   Function      :  static int FN_First_Field(FORM * form)
3680 |
3681 |   Description   :  Move to the first field on the current page of the form
3682 |
3683 |   Return Values :  E_OK                 - success
3684 |                    != E_OK              - error from subordinate call
3685 +--------------------------------------------------------------------------*/
3686 static int
FN_First_Field(FORM * form)3687 FN_First_Field(FORM *form)
3688 {
3689   T((T_CALLED("FN_First_Field(%p)"), (void *)form));
3690   returnCode(_nc_Set_Current_Field(form,
3691 				   Next_Field_On_Page(form->field[form->page[form->curpage].pmax])));
3692 }
3693 
3694 /*---------------------------------------------------------------------------
3695 |   Facility      :  libnform
3696 |   Function      :  static int FN_Last_Field(FORM * form)
3697 |
3698 |   Description   :  Move to the last field on the current page of the form
3699 |
3700 |   Return Values :  E_OK                 - success
3701 |                    != E_OK              - error from subordinate call
3702 +--------------------------------------------------------------------------*/
3703 static int
FN_Last_Field(FORM * form)3704 FN_Last_Field(FORM *form)
3705 {
3706   T((T_CALLED("FN_Last_Field(%p)"), (void *)form));
3707   returnCode(
3708 	      _nc_Set_Current_Field(form,
3709 				    Previous_Field_On_Page(form->field[form->page[form->curpage].pmin])));
3710 }
3711 
3712 /*---------------------------------------------------------------------------
3713 |   Facility      :  libnform
3714 |   Function      :  static int FN_Sorted_Next_Field(FORM * form)
3715 |
3716 |   Description   :  Move to the sorted next field on the current page
3717 |                    of the form.
3718 |
3719 |   Return Values :  E_OK            - success
3720 |                    != E_OK         - error from subordinate call
3721 +--------------------------------------------------------------------------*/
3722 static int
FN_Sorted_Next_Field(FORM * form)3723 FN_Sorted_Next_Field(FORM *form)
3724 {
3725   T((T_CALLED("FN_Sorted_Next_Field(%p)"), (void *)form));
3726   returnCode(_nc_Set_Current_Field(form,
3727 				   Sorted_Next_Field(form->current)));
3728 }
3729 
3730 /*---------------------------------------------------------------------------
3731 |   Facility      :  libnform
3732 |   Function      :  static int FN_Sorted_Previous_Field(FORM * form)
3733 |
3734 |   Description   :  Move to the sorted previous field on the current page
3735 |                    of the form.
3736 |
3737 |   Return Values :  E_OK            - success
3738 |                    != E_OK         - error from subordinate call
3739 +--------------------------------------------------------------------------*/
3740 static int
FN_Sorted_Previous_Field(FORM * form)3741 FN_Sorted_Previous_Field(FORM *form)
3742 {
3743   T((T_CALLED("FN_Sorted_Previous_Field(%p)"), (void *)form));
3744   returnCode(_nc_Set_Current_Field(form,
3745 				   Sorted_Previous_Field(form->current)));
3746 }
3747 
3748 /*---------------------------------------------------------------------------
3749 |   Facility      :  libnform
3750 |   Function      :  static int FN_Sorted_First_Field(FORM * form)
3751 |
3752 |   Description   :  Move to the sorted first field on the current page
3753 |                    of the form.
3754 |
3755 |   Return Values :  E_OK            - success
3756 |                    != E_OK         - error from subordinate call
3757 +--------------------------------------------------------------------------*/
3758 static int
FN_Sorted_First_Field(FORM * form)3759 FN_Sorted_First_Field(FORM *form)
3760 {
3761   T((T_CALLED("FN_Sorted_First_Field(%p)"), (void *)form));
3762   returnCode(_nc_Set_Current_Field(form,
3763 				   Sorted_Next_Field(form->field[form->page[form->curpage].smax])));
3764 }
3765 
3766 /*---------------------------------------------------------------------------
3767 |   Facility      :  libnform
3768 |   Function      :  static int FN_Sorted_Last_Field(FORM * form)
3769 |
3770 |   Description   :  Move to the sorted last field on the current page
3771 |                    of the form.
3772 |
3773 |   Return Values :  E_OK            - success
3774 |                    != E_OK         - error from subordinate call
3775 +--------------------------------------------------------------------------*/
3776 static int
FN_Sorted_Last_Field(FORM * form)3777 FN_Sorted_Last_Field(FORM *form)
3778 {
3779   T((T_CALLED("FN_Sorted_Last_Field(%p)"), (void *)form));
3780   returnCode(_nc_Set_Current_Field(form,
3781 				   Sorted_Previous_Field(form->field[form->page[form->curpage].smin])));
3782 }
3783 
3784 /*---------------------------------------------------------------------------
3785 |   Facility      :  libnform
3786 |   Function      :  static int FN_Left_Field(FORM * form)
3787 |
3788 |   Description   :  Get the field on the left of the current field on the
3789 |                    same line and the same page. Cycles through the line.
3790 |
3791 |   Return Values :  E_OK            - success
3792 |                    != E_OK         - error from subordinate call
3793 +--------------------------------------------------------------------------*/
3794 static int
FN_Left_Field(FORM * form)3795 FN_Left_Field(FORM *form)
3796 {
3797   T((T_CALLED("FN_Left_Field(%p)"), (void *)form));
3798   returnCode(_nc_Set_Current_Field(form,
3799 				   Left_Neighbor_Field(form->current)));
3800 }
3801 
3802 /*---------------------------------------------------------------------------
3803 |   Facility      :  libnform
3804 |   Function      :  static int FN_Right_Field(FORM * form)
3805 |
3806 |   Description   :  Get the field on the right of the current field on the
3807 |                    same line and the same page. Cycles through the line.
3808 |
3809 |   Return Values :  E_OK            - success
3810 |                    != E_OK         - error from subordinate call
3811 +--------------------------------------------------------------------------*/
3812 static int
FN_Right_Field(FORM * form)3813 FN_Right_Field(FORM *form)
3814 {
3815   T((T_CALLED("FN_Right_Field(%p)"), (void *)form));
3816   returnCode(_nc_Set_Current_Field(form,
3817 				   Right_Neighbor_Field(form->current)));
3818 }
3819 
3820 /*---------------------------------------------------------------------------
3821 |   Facility      :  libnform
3822 |   Function      :  static int FN_Up_Field(FORM * form)
3823 |
3824 |   Description   :  Get the upper neighbor of the current field. This
3825 |                    cycles through the page. See the comments of the
3826 |                    Upper_Neighbor_Field function to understand how
3827 |                    'upper' is defined.
3828 |
3829 |   Return Values :  E_OK            - success
3830 |                    != E_OK         - error from subordinate call
3831 +--------------------------------------------------------------------------*/
3832 static int
FN_Up_Field(FORM * form)3833 FN_Up_Field(FORM *form)
3834 {
3835   T((T_CALLED("FN_Up_Field(%p)"), (void *)form));
3836   returnCode(_nc_Set_Current_Field(form,
3837 				   Upper_Neighbor_Field(form->current)));
3838 }
3839 
3840 /*---------------------------------------------------------------------------
3841 |   Facility      :  libnform
3842 |   Function      :  static int FN_Down_Field(FORM * form)
3843 |
3844 |   Description   :  Get the down neighbor of the current field. This
3845 |                    cycles through the page. See the comments of the
3846 |                    Down_Neighbor_Field function to understand how
3847 |                    'down' is defined.
3848 |
3849 |   Return Values :  E_OK            - success
3850 |                    != E_OK         - error from subordinate call
3851 +--------------------------------------------------------------------------*/
3852 static int
FN_Down_Field(FORM * form)3853 FN_Down_Field(FORM *form)
3854 {
3855   T((T_CALLED("FN_Down_Field(%p)"), (void *)form));
3856   returnCode(_nc_Set_Current_Field(form,
3857 				   Down_Neighbor_Field(form->current)));
3858 }
3859 /*----------------------------------------------------------------------------
3860   END of Field Navigation routines
3861   --------------------------------------------------------------------------*/
3862 
3863 /*----------------------------------------------------------------------------
3864   Helper routines for Page Navigation
3865   --------------------------------------------------------------------------*/
3866 
3867 /*---------------------------------------------------------------------------
3868 |   Facility      :  libnform
3869 |   Function      :  int _nc_Set_Form_Page(FORM * form,
3870 |                                          int page,
3871 |                                          FIELD * field)
3872 |
3873 |   Description   :  Make the given page number the current page and make
3874 |                    the given field the current field on the page. If
3875 |                    for the field NULL is given, make the first field on
3876 |                    the page the current field. The routine acts only
3877 |                    if the requested page is not the current page.
3878 |
3879 |   Return Values :  E_OK                - success
3880 |                    != E_OK             - error from subordinate call
3881 |                    E_BAD_ARGUMENT      - invalid field pointer
3882 |                    E_SYSTEM_ERROR      - some severe basic error
3883 +--------------------------------------------------------------------------*/
3884 FORM_EXPORT(int)
_nc_Set_Form_Page(FORM * form,int page,FIELD * field)3885 _nc_Set_Form_Page(FORM *form, int page, FIELD *field)
3886 {
3887   int res = E_OK;
3888 
3889   if ((form->curpage != page))
3890     {
3891       FIELD *last_field, *field_on_page;
3892 
3893       werase(Get_Form_Window(form));
3894       form->curpage = (short)page;
3895       last_field = field_on_page = form->field[form->page[page].smin];
3896       do
3897 	{
3898 	  if ((unsigned)field_on_page->opts & O_VISIBLE)
3899 	    if ((res = Display_Field(field_on_page)) != E_OK)
3900 	      return (res);
3901 	  field_on_page = field_on_page->snext;
3902 	}
3903       while (field_on_page != last_field);
3904 
3905       if (field)
3906 	res = _nc_Set_Current_Field(form, field);
3907       else
3908 	/* N.B.: we don't encapsulate this by Inter_Field_Navigation(),
3909 	   because this is already executed in a page navigation
3910 	   context that contains field navigation
3911 	 */
3912 	res = FN_First_Field(form);
3913     }
3914   return (res);
3915 }
3916 
3917 /*---------------------------------------------------------------------------
3918 |   Facility      :  libnform
3919 |   Function      :  static int Next_Page_Number(const FORM * form)
3920 |
3921 |   Description   :  Calculate the page number following the current page
3922 |                    number. This cycles if the highest page number is
3923 |                    reached.
3924 |
3925 |   Return Values :  The next page number
3926 +--------------------------------------------------------------------------*/
3927 NCURSES_INLINE static int
Next_Page_Number(const FORM * form)3928 Next_Page_Number(const FORM *form)
3929 {
3930   return (form->curpage + 1) % form->maxpage;
3931 }
3932 
3933 /*---------------------------------------------------------------------------
3934 |   Facility      :  libnform
3935 |   Function      :  static int Previous_Page_Number(const FORM * form)
3936 |
3937 |   Description   :  Calculate the page number before the current page
3938 |                    number. This cycles if the first page number is
3939 |                    reached.
3940 |
3941 |   Return Values :  The previous page number
3942 +--------------------------------------------------------------------------*/
3943 NCURSES_INLINE static int
Previous_Page_Number(const FORM * form)3944 Previous_Page_Number(const FORM *form)
3945 {
3946   return (form->curpage != 0 ? form->curpage - 1 : form->maxpage - 1);
3947 }
3948 
3949 /*----------------------------------------------------------------------------
3950   Page Navigation routines
3951   --------------------------------------------------------------------------*/
3952 
3953 /*---------------------------------------------------------------------------
3954 |   Facility      :  libnform
3955 |   Function      :  static int Page_Navigation(
3956 |                                               int (* const fct) (FORM *),
3957 |                                               FORM * form)
3958 |
3959 |   Description   :  Generic behavior for changing a page. This means
3960 |                    that the field is left and a new field is entered.
3961 |                    So the field must be validated and the field init/term
3962 |                    hooks must be called. Because also the page is changed,
3963 |                    the form's init/term hooks must be called also.
3964 |
3965 |   Return Values :  E_OK                - success
3966 |                    E_INVALID_FIELD     - field is invalid
3967 |                    some other          - error from subordinate call
3968 +--------------------------------------------------------------------------*/
3969 static int
Page_Navigation(int (* const fct)(FORM *),FORM * form)3970 Page_Navigation(int (*const fct) (FORM *), FORM *form)
3971 {
3972   int res;
3973 
3974   if (!_nc_Internal_Validation(form))
3975     res = E_INVALID_FIELD;
3976   else
3977     {
3978       Call_Hook(form, fieldterm);
3979       Call_Hook(form, formterm);
3980       res = fct(form);
3981       Call_Hook(form, forminit);
3982       Call_Hook(form, fieldinit);
3983     }
3984   return res;
3985 }
3986 
3987 /*---------------------------------------------------------------------------
3988 |   Facility      :  libnform
3989 |   Function      :  static int PN_Next_Page(FORM * form)
3990 |
3991 |   Description   :  Move to the next page of the form
3992 |
3993 |   Return Values :  E_OK                - success
3994 |                    != E_OK             - error from subordinate call
3995 +--------------------------------------------------------------------------*/
3996 static int
PN_Next_Page(FORM * form)3997 PN_Next_Page(FORM *form)
3998 {
3999   T((T_CALLED("PN_Next_Page(%p)"), (void *)form));
4000   returnCode(_nc_Set_Form_Page(form, Next_Page_Number(form), (FIELD *)0));
4001 }
4002 
4003 /*---------------------------------------------------------------------------
4004 |   Facility      :  libnform
4005 |   Function      :  static int PN_Previous_Page(FORM * form)
4006 |
4007 |   Description   :  Move to the previous page of the form
4008 |
4009 |   Return Values :  E_OK              - success
4010 |                    != E_OK           - error from subordinate call
4011 +--------------------------------------------------------------------------*/
4012 static int
PN_Previous_Page(FORM * form)4013 PN_Previous_Page(FORM *form)
4014 {
4015   T((T_CALLED("PN_Previous_Page(%p)"), (void *)form));
4016   returnCode(_nc_Set_Form_Page(form, Previous_Page_Number(form), (FIELD *)0));
4017 }
4018 
4019 /*---------------------------------------------------------------------------
4020 |   Facility      :  libnform
4021 |   Function      :  static int PN_First_Page(FORM * form)
4022 |
4023 |   Description   :  Move to the first page of the form
4024 |
4025 |   Return Values :  E_OK              - success
4026 |                    != E_OK           - error from subordinate call
4027 +--------------------------------------------------------------------------*/
4028 static int
PN_First_Page(FORM * form)4029 PN_First_Page(FORM *form)
4030 {
4031   T((T_CALLED("PN_First_Page(%p)"), (void *)form));
4032   returnCode(_nc_Set_Form_Page(form, 0, (FIELD *)0));
4033 }
4034 
4035 /*---------------------------------------------------------------------------
4036 |   Facility      :  libnform
4037 |   Function      :  static int PN_Last_Page(FORM * form)
4038 |
4039 |   Description   :  Move to the last page of the form
4040 |
4041 |   Return Values :  E_OK              - success
4042 |                    != E_OK           - error from subordinate call
4043 +--------------------------------------------------------------------------*/
4044 static int
PN_Last_Page(FORM * form)4045 PN_Last_Page(FORM *form)
4046 {
4047   T((T_CALLED("PN_Last_Page(%p)"), (void *)form));
4048   returnCode(_nc_Set_Form_Page(form, form->maxpage - 1, (FIELD *)0));
4049 }
4050 
4051 /*----------------------------------------------------------------------------
4052   END of Field Navigation routines
4053   --------------------------------------------------------------------------*/
4054 
4055 /*----------------------------------------------------------------------------
4056   Helper routines for the core form driver.
4057   --------------------------------------------------------------------------*/
4058 
4059 # if USE_WIDEC_SUPPORT
4060 /*---------------------------------------------------------------------------
4061 |   Facility      :  libnform
4062 |   Function      :  static int Data_Entry_w(FORM * form, wchar_t c)
4063 |
4064 |   Description   :  Enter the wide character c into at the current
4065 |                    position of the current field of the form.
4066 |
4067 |   Return Values :  E_OK              - success
4068 |                    E_REQUEST_DENIED  - driver could not process the request
4069 |                    E_SYSTEM_ERROR    -
4070 +--------------------------------------------------------------------------*/
4071 static int
Data_Entry_w(FORM * form,wchar_t c)4072 Data_Entry_w(FORM *form, wchar_t c)
4073 {
4074   FIELD *field = form->current;
4075   int result = E_REQUEST_DENIED;
4076 
4077   T((T_CALLED("Data_Entry(%p,%s)"), (void *)form, _tracechtype((chtype)c)));
4078   if ((Field_Has_Option(field, O_EDIT))
4079 #if FIX_FORM_INACTIVE_BUG
4080       && (Field_Has_Option(field, O_ACTIVE))
4081 #endif
4082     )
4083     {
4084       wchar_t given[2];
4085       cchar_t temp_ch;
4086 
4087       given[0] = c;
4088       given[1] = 0;
4089       setcchar(&temp_ch, given, 0, 0, (void *)0);
4090       if ((Field_Has_Option(field, O_BLANK)) &&
4091 	  First_Position_In_Current_Field(form) &&
4092 	  !(form->status & _FCHECK_REQUIRED) &&
4093 	  !(form->status & _WINDOW_MODIFIED))
4094 	werase(form->w);
4095 
4096       if (form->status & _OVLMODE)
4097 	{
4098 	  wadd_wch(form->w, &temp_ch);
4099 	}
4100       else
4101 	/* no _OVLMODE */
4102 	{
4103 	  bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
4104 
4105 	  if (!(There_Is_Room ||
4106 		((Single_Line_Field(field) && Growable(field)))))
4107 	    RETURN(E_REQUEST_DENIED);
4108 
4109 	  if (!There_Is_Room && !Field_Grown(field, 1))
4110 	    RETURN(E_SYSTEM_ERROR);
4111 
4112 	  wins_wch(form->w, &temp_ch);
4113 	}
4114 
4115       if ((result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form)) == E_OK)
4116 	{
4117 	  bool End_Of_Field = (((field->drows - 1) == form->currow) &&
4118 			       ((field->dcols - 1) == form->curcol));
4119 
4120 	  form->status |= _WINDOW_MODIFIED;
4121 	  if (End_Of_Field && !Growable(field) && (Field_Has_Option(field, O_AUTOSKIP)))
4122 	    result = Inter_Field_Navigation(FN_Next_Field, form);
4123 	  else
4124 	    {
4125 	      if (End_Of_Field && Growable(field) && !Field_Grown(field, 1))
4126 		result = E_SYSTEM_ERROR;
4127 	      else
4128 		{
4129 		  /*
4130 		   * We have just added a byte to the form field.  It may have
4131 		   * been part of a multibyte character.  If it was, the
4132 		   * addch_used field is nonzero and we should not try to move
4133 		   * to a new column.
4134 		   */
4135 		  if (WINDOW_EXT(form->w, addch_used) == 0)
4136 		    IFN_Next_Character(form);
4137 
4138 		  result = E_OK;
4139 		}
4140 	    }
4141 	}
4142     }
4143   RETURN(result);
4144 }
4145 # endif
4146 
4147 /*---------------------------------------------------------------------------
4148 |   Facility      :  libnform
4149 |   Function      :  static int Data_Entry(FORM * form,int c)
4150 |
4151 |   Description   :  Enter character c into at the current position of the
4152 |                    current field of the form.
4153 |
4154 |   Return Values :  E_OK              - success
4155 |                    E_REQUEST_DENIED  - driver could not process the request
4156 |                    E_SYSTEM_ERROR    -
4157 +--------------------------------------------------------------------------*/
4158 static int
Data_Entry(FORM * form,int c)4159 Data_Entry(FORM *form, int c)
4160 {
4161   FIELD *field = form->current;
4162   int result = E_REQUEST_DENIED;
4163 
4164   T((T_CALLED("Data_Entry(%p,%s)"), (void *)form, _tracechtype((chtype)c)));
4165   if ((Field_Has_Option(field, O_EDIT))
4166 #if FIX_FORM_INACTIVE_BUG
4167       && (Field_Has_Option(field, O_ACTIVE))
4168 #endif
4169     )
4170     {
4171       if ((Field_Has_Option(field, O_BLANK)) &&
4172 	  First_Position_In_Current_Field(form) &&
4173 	  !(form->status & _FCHECK_REQUIRED) &&
4174 	  !(form->status & _WINDOW_MODIFIED))
4175 	werase(form->w);
4176 
4177       if (form->status & _OVLMODE)
4178 	{
4179 	  waddch(form->w, (chtype)c);
4180 	}
4181       else
4182 	/* no _OVLMODE */
4183 	{
4184 	  bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
4185 
4186 	  if (!(There_Is_Room ||
4187 		((Single_Line_Field(field) && Growable(field)))))
4188 	    RETURN(E_REQUEST_DENIED);
4189 
4190 	  if (!There_Is_Room && !Field_Grown(field, 1))
4191 	    RETURN(E_SYSTEM_ERROR);
4192 
4193 	  winsch(form->w, (chtype)c);
4194 	}
4195 
4196       if ((result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form)) == E_OK)
4197 	{
4198 	  bool End_Of_Field = (((field->drows - 1) == form->currow) &&
4199 			       ((field->dcols - 1) == form->curcol));
4200 
4201 	  if (Field_Has_Option(field, O_EDGE_INSERT_STAY))
4202 	    move_after_insert = !!(form->curcol
4203 				   - form->begincol
4204 				   - field->cols
4205 				   + 1);
4206 
4207 	  SetStatus(form, _WINDOW_MODIFIED);
4208 	  if (End_Of_Field && !Growable(field) && (Field_Has_Option(field, O_AUTOSKIP)))
4209 	    result = Inter_Field_Navigation(FN_Next_Field, form);
4210 	  else
4211 	    {
4212 	      if (End_Of_Field && Growable(field) && !Field_Grown(field, 1))
4213 		result = E_SYSTEM_ERROR;
4214 	      else
4215 		{
4216 #if USE_WIDEC_SUPPORT
4217 		  /*
4218 		   * We have just added a byte to the form field.  It may have
4219 		   * been part of a multibyte character.  If it was, the
4220 		   * addch_used field is nonzero and we should not try to move
4221 		   * to a new column.
4222 		   */
4223 		  if (WINDOW_EXT(form->w, addch_used) == 0)
4224 		    IFN_Next_Character(form);
4225 #else
4226 		  IFN_Next_Character(form);
4227 #endif
4228 		  result = E_OK;
4229 		}
4230 	    }
4231 	}
4232     }
4233   RETURN(result);
4234 }
4235 
4236 /* Structure to describe the binding of a request code to a function.
4237    The member keycode codes the request value as well as the generic
4238    routine to use for the request. The code for the generic routine
4239    is coded in the upper 16 Bits while the request code is coded in
4240    the lower 16 bits.
4241 
4242    In terms of C++ you might think of a request as a class with a
4243    virtual method "perform". The different types of request are
4244    derived from this base class and overload (or not) the base class
4245    implementation of perform.
4246 */
4247 typedef struct
4248 {
4249   int keycode;			/* must be at least 32 bit: hi:mode, lo: key */
4250   int (*cmd) (FORM *);		/* low level driver routine for this key     */
4251 }
4252 Binding_Info;
4253 
4254 /* You may see this is the class-id of the request type class */
4255 #define ID_PN    (0x00000000)	/* Page navigation           */
4256 #define ID_FN    (0x00010000)	/* Inter-Field navigation    */
4257 #define ID_IFN   (0x00020000)	/* Intra-Field navigation    */
4258 #define ID_VSC   (0x00030000)	/* Vertical Scrolling        */
4259 #define ID_HSC   (0x00040000)	/* Horizontal Scrolling      */
4260 #define ID_FE    (0x00050000)	/* Field Editing             */
4261 #define ID_EM    (0x00060000)	/* Edit Mode                 */
4262 #define ID_FV    (0x00070000)	/* Field Validation          */
4263 #define ID_CH    (0x00080000)	/* Choice                    */
4264 #define ID_Mask  (0xffff0000)
4265 #define Key_Mask (0x0000ffff)
4266 #define ID_Shft  (16)
4267 
4268 /* This array holds all the Binding Infos */
4269 /* *INDENT-OFF* */
4270 static const Binding_Info bindings[MAX_FORM_COMMAND - MIN_FORM_COMMAND + 1] =
4271 {
4272   { REQ_NEXT_PAGE    |ID_PN  ,PN_Next_Page},
4273   { REQ_PREV_PAGE    |ID_PN  ,PN_Previous_Page},
4274   { REQ_FIRST_PAGE   |ID_PN  ,PN_First_Page},
4275   { REQ_LAST_PAGE    |ID_PN  ,PN_Last_Page},
4276 
4277   { REQ_NEXT_FIELD   |ID_FN  ,FN_Next_Field},
4278   { REQ_PREV_FIELD   |ID_FN  ,FN_Previous_Field},
4279   { REQ_FIRST_FIELD  |ID_FN  ,FN_First_Field},
4280   { REQ_LAST_FIELD   |ID_FN  ,FN_Last_Field},
4281   { REQ_SNEXT_FIELD  |ID_FN  ,FN_Sorted_Next_Field},
4282   { REQ_SPREV_FIELD  |ID_FN  ,FN_Sorted_Previous_Field},
4283   { REQ_SFIRST_FIELD |ID_FN  ,FN_Sorted_First_Field},
4284   { REQ_SLAST_FIELD  |ID_FN  ,FN_Sorted_Last_Field},
4285   { REQ_LEFT_FIELD   |ID_FN  ,FN_Left_Field},
4286   { REQ_RIGHT_FIELD  |ID_FN  ,FN_Right_Field},
4287   { REQ_UP_FIELD     |ID_FN  ,FN_Up_Field},
4288   { REQ_DOWN_FIELD   |ID_FN  ,FN_Down_Field},
4289 
4290   { REQ_NEXT_CHAR    |ID_IFN ,IFN_Next_Character},
4291   { REQ_PREV_CHAR    |ID_IFN ,IFN_Previous_Character},
4292   { REQ_NEXT_LINE    |ID_IFN ,IFN_Next_Line},
4293   { REQ_PREV_LINE    |ID_IFN ,IFN_Previous_Line},
4294   { REQ_NEXT_WORD    |ID_IFN ,IFN_Next_Word},
4295   { REQ_PREV_WORD    |ID_IFN ,IFN_Previous_Word},
4296   { REQ_BEG_FIELD    |ID_IFN ,IFN_Beginning_Of_Field},
4297   { REQ_END_FIELD    |ID_IFN ,IFN_End_Of_Field},
4298   { REQ_BEG_LINE     |ID_IFN ,IFN_Beginning_Of_Line},
4299   { REQ_END_LINE     |ID_IFN ,IFN_End_Of_Line},
4300   { REQ_LEFT_CHAR    |ID_IFN ,IFN_Left_Character},
4301   { REQ_RIGHT_CHAR   |ID_IFN ,IFN_Right_Character},
4302   { REQ_UP_CHAR      |ID_IFN ,IFN_Up_Character},
4303   { REQ_DOWN_CHAR    |ID_IFN ,IFN_Down_Character},
4304 
4305   { REQ_NEW_LINE     |ID_FE  ,FE_New_Line},
4306   { REQ_INS_CHAR     |ID_FE  ,FE_Insert_Character},
4307   { REQ_INS_LINE     |ID_FE  ,FE_Insert_Line},
4308   { REQ_DEL_CHAR     |ID_FE  ,FE_Delete_Character},
4309   { REQ_DEL_PREV     |ID_FE  ,FE_Delete_Previous},
4310   { REQ_DEL_LINE     |ID_FE  ,FE_Delete_Line},
4311   { REQ_DEL_WORD     |ID_FE  ,FE_Delete_Word},
4312   { REQ_CLR_EOL      |ID_FE  ,FE_Clear_To_End_Of_Line},
4313   { REQ_CLR_EOF      |ID_FE  ,FE_Clear_To_End_Of_Field},
4314   { REQ_CLR_FIELD    |ID_FE  ,FE_Clear_Field},
4315 
4316   { REQ_OVL_MODE     |ID_EM  ,EM_Overlay_Mode},
4317   { REQ_INS_MODE     |ID_EM  ,EM_Insert_Mode},
4318 
4319   { REQ_SCR_FLINE    |ID_VSC ,VSC_Scroll_Line_Forward},
4320   { REQ_SCR_BLINE    |ID_VSC ,VSC_Scroll_Line_Backward},
4321   { REQ_SCR_FPAGE    |ID_VSC ,VSC_Scroll_Page_Forward},
4322   { REQ_SCR_BPAGE    |ID_VSC ,VSC_Scroll_Page_Backward},
4323   { REQ_SCR_FHPAGE   |ID_VSC ,VSC_Scroll_Half_Page_Forward},
4324   { REQ_SCR_BHPAGE   |ID_VSC ,VSC_Scroll_Half_Page_Backward},
4325 
4326   { REQ_SCR_FCHAR    |ID_HSC ,HSC_Scroll_Char_Forward},
4327   { REQ_SCR_BCHAR    |ID_HSC ,HSC_Scroll_Char_Backward},
4328   { REQ_SCR_HFLINE   |ID_HSC ,HSC_Horizontal_Line_Forward},
4329   { REQ_SCR_HBLINE   |ID_HSC ,HSC_Horizontal_Line_Backward},
4330   { REQ_SCR_HFHALF   |ID_HSC ,HSC_Horizontal_Half_Line_Forward},
4331   { REQ_SCR_HBHALF   |ID_HSC ,HSC_Horizontal_Half_Line_Backward},
4332 
4333   { REQ_VALIDATION   |ID_FV  ,FV_Validation},
4334 
4335   { REQ_NEXT_CHOICE  |ID_CH  ,CR_Next_Choice},
4336   { REQ_PREV_CHOICE  |ID_CH  ,CR_Previous_Choice}
4337 };
4338 /* *INDENT-ON* */
4339 
4340 /*---------------------------------------------------------------------------
4341 |   Facility      :  libnform
4342 |   Function      :  int form_driver(FORM * form,int  c)
4343 |
4344 |   Description   :  This is the workhorse of the forms system. It checks
4345 |                    to determine whether the character c is a request or
4346 |                    data. If it is a request, the form driver executes
4347 |                    the request and returns the result. If it is data
4348 |                    (printable character), it enters the data into the
4349 |                    current position in the current field. If it is not
4350 |                    recognized, the form driver assumes it is an application
4351 |                    defined command and returns E_UNKNOWN_COMMAND.
4352 |                    Application defined command should be defined relative
4353 |                    to MAX_FORM_COMMAND, the maximum value of a request.
4354 |
4355 |   Return Values :  E_OK              - success
4356 |                    E_SYSTEM_ERROR    - system error
4357 |                    E_BAD_ARGUMENT    - an argument is incorrect
4358 |                    E_NOT_POSTED      - form is not posted
4359 |                    E_INVALID_FIELD   - field contents are invalid
4360 |                    E_BAD_STATE       - called from inside a hook routine
4361 |                    E_REQUEST_DENIED  - request failed
4362 |                    E_NOT_CONNECTED   - no fields are connected to the form
4363 |                    E_UNKNOWN_COMMAND - command not known
4364 +--------------------------------------------------------------------------*/
4365 FORM_EXPORT(int)
form_driver(FORM * form,int c)4366 form_driver(FORM *form, int c)
4367 {
4368   const Binding_Info *BI = (Binding_Info *)0;
4369   int res = E_UNKNOWN_COMMAND;
4370 
4371   move_after_insert = TRUE;
4372 
4373   T((T_CALLED("form_driver(%p,%d)"), (void *)form, c));
4374 
4375   if (!form)
4376     RETURN(E_BAD_ARGUMENT);
4377 
4378   if (!(form->field) || !(form->current))
4379     RETURN(E_NOT_CONNECTED);
4380 
4381   assert(form->page);
4382 
4383   if (c == FIRST_ACTIVE_MAGIC)
4384     {
4385       form->current = _nc_First_Active_Field(form);
4386       RETURN(E_OK);
4387     }
4388 
4389   assert(form->current &&
4390 	 form->current->buf &&
4391 	 (form->current->form == form)
4392     );
4393 
4394   if (form->status & _IN_DRIVER)
4395     RETURN(E_BAD_STATE);
4396 
4397   if (!(form->status & _POSTED))
4398     RETURN(E_NOT_POSTED);
4399 
4400   if ((c >= MIN_FORM_COMMAND && c <= MAX_FORM_COMMAND) &&
4401       ((bindings[c - MIN_FORM_COMMAND].keycode & Key_Mask) == c))
4402     {
4403       TR(TRACE_CALLS, ("form_request %s", form_request_name(c)));
4404       BI = &(bindings[c - MIN_FORM_COMMAND]);
4405     }
4406 
4407   if (BI)
4408     {
4409       typedef int (*Generic_Method) (int (*const) (FORM *), FORM *);
4410       static const Generic_Method Generic_Methods[] =
4411       {
4412 	Page_Navigation,	/* overloaded to call field&form hooks */
4413 	Inter_Field_Navigation,	/* overloaded to call field hooks      */
4414 	NULL,			/* Intra-Field is generic              */
4415 	Vertical_Scrolling,	/* Overloaded to check multi-line      */
4416 	Horizontal_Scrolling,	/* Overloaded to check single-line     */
4417 	Field_Editing,		/* Overloaded to mark modification     */
4418 	NULL,			/* Edit Mode is generic                */
4419 	NULL,			/* Field Validation is generic         */
4420 	NULL			/* Choice Request is generic           */
4421       };
4422       size_t nMethods = (sizeof(Generic_Methods) / sizeof(Generic_Methods[0]));
4423       size_t method = (size_t)((BI->keycode >> ID_Shft) & 0xffff);	/* see ID_Mask */
4424 
4425       if ((method >= nMethods) || !(BI->cmd))
4426 	res = E_SYSTEM_ERROR;
4427       else
4428 	{
4429 	  Generic_Method fct = Generic_Methods[method];
4430 
4431 	  if (fct)
4432 	    {
4433 	      res = fct(BI->cmd, form);
4434 	    }
4435 	  else
4436 	    {
4437 	      res = (BI->cmd) (form);
4438 	    }
4439 	}
4440     }
4441 #ifdef NCURSES_MOUSE_VERSION
4442   else if (KEY_MOUSE == c)
4443     {
4444       MEVENT event;
4445       WINDOW *win = form->win ? form->win : StdScreen(Get_Form_Screen(form));
4446       const WINDOW *sub = form->sub ? form->sub : win;
4447 
4448       getmouse(&event);
4449       if ((event.bstate & (BUTTON1_CLICKED |
4450 			   BUTTON1_DOUBLE_CLICKED |
4451 			   BUTTON1_TRIPLE_CLICKED))
4452 	  && wenclose(win, event.y, event.x))
4453 	{			/* we react only if the click was in the userwin, that means
4454 				 * inside the form display area or at the decoration window.
4455 				 */
4456 	  int ry = event.y, rx = event.x;	/* screen coordinates */
4457 
4458 	  res = E_REQUEST_DENIED;
4459 	  if (mouse_trafo(&ry, &rx, FALSE))
4460 	    {			/* rx, ry are now "curses" coordinates */
4461 	      if (ry < sub->_begy)
4462 		{		/* we clicked above the display region; this is
4463 				 * interpreted as "scroll up" request
4464 				 */
4465 		  if (event.bstate & BUTTON1_CLICKED)
4466 		    res = form_driver(form, REQ_PREV_FIELD);
4467 		  else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4468 		    res = form_driver(form, REQ_PREV_PAGE);
4469 		  else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4470 		    res = form_driver(form, REQ_FIRST_FIELD);
4471 		}
4472 	      else if (ry > sub->_begy + sub->_maxy)
4473 		{		/* we clicked below the display region; this is
4474 				 * interpreted as "scroll down" request
4475 				 */
4476 		  if (event.bstate & BUTTON1_CLICKED)
4477 		    res = form_driver(form, REQ_NEXT_FIELD);
4478 		  else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4479 		    res = form_driver(form, REQ_NEXT_PAGE);
4480 		  else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4481 		    res = form_driver(form, REQ_LAST_FIELD);
4482 		}
4483 	      else if (wenclose(sub, event.y, event.x))
4484 		{		/* Inside the area we try to find the hit item */
4485 		  ry = event.y;
4486 		  rx = event.x;
4487 		  if (wmouse_trafo(sub, &ry, &rx, FALSE))
4488 		    {
4489 		      int min_field = form->page[form->curpage].pmin;
4490 		      int max_field = form->page[form->curpage].pmax;
4491 		      int i;
4492 
4493 		      for (i = min_field; i <= max_field; ++i)
4494 			{
4495 			  FIELD *field = form->field[i];
4496 
4497 			  if (Field_Is_Selectable(field)
4498 			      && Field_encloses(field, ry, rx) == E_OK)
4499 			    {
4500 			      res = _nc_Set_Current_Field(form, field);
4501 			      if (res == E_OK)
4502 				res = _nc_Position_Form_Cursor(form);
4503 			      if (res == E_OK
4504 				  && (event.bstate & BUTTON1_DOUBLE_CLICKED))
4505 				res = E_UNKNOWN_COMMAND;
4506 			      break;
4507 			    }
4508 			}
4509 		    }
4510 		}
4511 	    }
4512 	}
4513       else
4514 	res = E_REQUEST_DENIED;
4515     }
4516 #endif /* NCURSES_MOUSE_VERSION */
4517   else if (!(c & (~(int)MAX_REGULAR_CHARACTER)))
4518     {
4519       /*
4520        * If we're using 8-bit characters, iscntrl+isprint cover the whole set.
4521        * But with multibyte characters, there is a third possibility, i.e.,
4522        * parts of characters that build up into printable characters which are
4523        * not considered printable.
4524        *
4525        * FIXME: the wide-character branch should also use Check_Char().
4526        */
4527 #if USE_WIDEC_SUPPORT
4528       if (!iscntrl(UChar(c)))
4529 #else
4530       if (isprint(UChar(c)) &&
4531 	  Check_Char(form, form->current, form->current->type, c,
4532 		     (TypeArgument *)(form->current->arg)))
4533 #endif
4534 	res = Data_Entry(form, c);
4535     }
4536   _nc_Refresh_Current_Field(form);
4537   RETURN(res);
4538 }
4539 
4540 # if USE_WIDEC_SUPPORT
4541 /*---------------------------------------------------------------------------
4542 |   Facility      :  libnform
4543 |   Function      :  int form_driver_w(FORM * form,int type,wchar_t  c)
4544 |
4545 |   Description   :  This is the workhorse of the forms system.
4546 |
4547 |                    Input is either a key code (request) or a wide char
4548 |                    returned by e.g. get_wch (). The type must be passed
4549 |                    as well,so that we are able to determine whether the char
4550 |                    is a multibyte char or a request.
4551 
4552 |                    If it is a request, the form driver executes
4553 |                    the request and returns the result. If it is data
4554 |                    (printable character), it enters the data into the
4555 |                    current position in the current field. If it is not
4556 |                    recognized, the form driver assumes it is an application
4557 |                    defined command and returns E_UNKNOWN_COMMAND.
4558 |                    Application defined command should be defined relative
4559 |                    to MAX_FORM_COMMAND, the maximum value of a request.
4560 |
4561 |   Return Values :  E_OK              - success
4562 |                    E_SYSTEM_ERROR    - system error
4563 |                    E_BAD_ARGUMENT    - an argument is incorrect
4564 |                    E_NOT_POSTED      - form is not posted
4565 |                    E_INVALID_FIELD   - field contents are invalid
4566 |                    E_BAD_STATE       - called from inside a hook routine
4567 |                    E_REQUEST_DENIED  - request failed
4568 |                    E_NOT_CONNECTED   - no fields are connected to the form
4569 |                    E_UNKNOWN_COMMAND - command not known
4570 +--------------------------------------------------------------------------*/
4571 FORM_EXPORT(int)
form_driver_w(FORM * form,int type,wchar_t c)4572 form_driver_w(FORM *form, int type, wchar_t c)
4573 {
4574   const Binding_Info *BI = (Binding_Info *)0;
4575   int res = E_UNKNOWN_COMMAND;
4576 
4577   T((T_CALLED("form_driver(%p,%d)"), (void *)form, (int)c));
4578 
4579   if (!form)
4580     RETURN(E_BAD_ARGUMENT);
4581 
4582   if (!(form->field))
4583     RETURN(E_NOT_CONNECTED);
4584 
4585   assert(form->page);
4586 
4587   if (c == (wchar_t)FIRST_ACTIVE_MAGIC)
4588     {
4589       form->current = _nc_First_Active_Field(form);
4590       RETURN(E_OK);
4591     }
4592 
4593   assert(form->current &&
4594 	 form->current->buf &&
4595 	 (form->current->form == form)
4596     );
4597 
4598   if (form->status & _IN_DRIVER)
4599     RETURN(E_BAD_STATE);
4600 
4601   if (!(form->status & _POSTED))
4602     RETURN(E_NOT_POSTED);
4603 
4604   /* check if this is a keycode or a (wide) char */
4605   if (type == KEY_CODE_YES)
4606     {
4607       if ((c >= MIN_FORM_COMMAND && c <= MAX_FORM_COMMAND) &&
4608 	  ((bindings[c - MIN_FORM_COMMAND].keycode & Key_Mask) == c))
4609 	BI = &(bindings[c - MIN_FORM_COMMAND]);
4610     }
4611 
4612   if (BI)
4613     {
4614       typedef int (*Generic_Method) (int (*const) (FORM *), FORM *);
4615       static const Generic_Method Generic_Methods[] =
4616       {
4617 	Page_Navigation,	/* overloaded to call field&form hooks */
4618 	Inter_Field_Navigation,	/* overloaded to call field hooks      */
4619 	NULL,			/* Intra-Field is generic              */
4620 	Vertical_Scrolling,	/* Overloaded to check multi-line      */
4621 	Horizontal_Scrolling,	/* Overloaded to check single-line     */
4622 	Field_Editing,		/* Overloaded to mark modification     */
4623 	NULL,			/* Edit Mode is generic                */
4624 	NULL,			/* Field Validation is generic         */
4625 	NULL			/* Choice Request is generic           */
4626       };
4627       size_t nMethods = (sizeof(Generic_Methods) / sizeof(Generic_Methods[0]));
4628       size_t method = (size_t)(BI->keycode >> ID_Shft) & 0xffff;	/* see ID_Mask */
4629 
4630       if ((method >= nMethods) || !(BI->cmd))
4631 	res = E_SYSTEM_ERROR;
4632       else
4633 	{
4634 	  Generic_Method fct = Generic_Methods[method];
4635 
4636 	  if (fct)
4637 	    res = fct(BI->cmd, form);
4638 	  else
4639 	    res = (BI->cmd) (form);
4640 	}
4641     }
4642 #ifdef NCURSES_MOUSE_VERSION
4643   else if (KEY_MOUSE == c)
4644     {
4645       MEVENT event;
4646       WINDOW *win = form->win ? form->win : StdScreen(Get_Form_Screen(form));
4647       const WINDOW *sub = form->sub ? form->sub : win;
4648 
4649       getmouse(&event);
4650       if ((event.bstate & (BUTTON1_CLICKED |
4651 			   BUTTON1_DOUBLE_CLICKED |
4652 			   BUTTON1_TRIPLE_CLICKED))
4653 	  && wenclose(win, event.y, event.x))
4654 	{			/* we react only if the click was in the userwin, that means
4655 				   * inside the form display area or at the decoration window.
4656 				 */
4657 	  int ry = event.y, rx = event.x;	/* screen coordinates */
4658 
4659 	  res = E_REQUEST_DENIED;
4660 	  if (mouse_trafo(&ry, &rx, FALSE))
4661 	    {			/* rx, ry are now "curses" coordinates */
4662 	      if (ry < sub->_begy)
4663 		{		/* we clicked above the display region; this is
4664 				   * interpreted as "scroll up" request
4665 				 */
4666 		  if (event.bstate & BUTTON1_CLICKED)
4667 		    res = form_driver(form, REQ_PREV_FIELD);
4668 		  else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4669 		    res = form_driver(form, REQ_PREV_PAGE);
4670 		  else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4671 		    res = form_driver(form, REQ_FIRST_FIELD);
4672 		}
4673 	      else if (ry > sub->_begy + sub->_maxy)
4674 		{		/* we clicked below the display region; this is
4675 				   * interpreted as "scroll down" request
4676 				 */
4677 		  if (event.bstate & BUTTON1_CLICKED)
4678 		    res = form_driver(form, REQ_NEXT_FIELD);
4679 		  else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4680 		    res = form_driver(form, REQ_NEXT_PAGE);
4681 		  else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4682 		    res = form_driver(form, REQ_LAST_FIELD);
4683 		}
4684 	      else if (wenclose(sub, event.y, event.x))
4685 		{		/* Inside the area we try to find the hit item */
4686 		  ry = event.y;
4687 		  rx = event.x;
4688 		  if (wmouse_trafo(sub, &ry, &rx, FALSE))
4689 		    {
4690 		      int min_field = form->page[form->curpage].pmin;
4691 		      int max_field = form->page[form->curpage].pmax;
4692 		      int i;
4693 
4694 		      for (i = min_field; i <= max_field; ++i)
4695 			{
4696 			  FIELD *field = form->field[i];
4697 
4698 			  if (Field_Is_Selectable(field)
4699 			      && Field_encloses(field, ry, rx) == E_OK)
4700 			    {
4701 			      res = _nc_Set_Current_Field(form, field);
4702 			      if (res == E_OK)
4703 				res = _nc_Position_Form_Cursor(form);
4704 			      if (res == E_OK
4705 				  && (event.bstate & BUTTON1_DOUBLE_CLICKED))
4706 				res = E_UNKNOWN_COMMAND;
4707 			      break;
4708 			    }
4709 			}
4710 		    }
4711 		}
4712 	    }
4713 	}
4714       else
4715 	res = E_REQUEST_DENIED;
4716     }
4717 #endif /* NCURSES_MOUSE_VERSION */
4718   else if (type == OK)
4719     {
4720       res = Data_Entry_w(form, c);
4721     }
4722 
4723   _nc_Refresh_Current_Field(form);
4724   RETURN(res);
4725 }
4726 # endif	/* USE_WIDEC_SUPPORT */
4727 
4728 /*----------------------------------------------------------------------------
4729   Field-Buffer manipulation routines.
4730   The effects of setting a buffer are tightly coupled to the core of the form
4731   driver logic. This is especially true in the case of growable fields.
4732   So I don't separate this into a separate module.
4733   --------------------------------------------------------------------------*/
4734 
4735 /*---------------------------------------------------------------------------
4736 |   Facility      :  libnform
4737 |   Function      :  int set_field_buffer(FIELD *field,
4738 |                                         int buffer, char *value)
4739 |
4740 |   Description   :  Set the given buffer of the field to the given value.
4741 |                    Buffer 0 stores the displayed content of the field.
4742 |                    For dynamic fields this may grow the fieldbuffers if
4743 |                    the length of the value exceeds the current buffer
4744 |                    length. For buffer 0 only printable values are allowed.
4745 |                    For static fields, the value must not be zero terminated.
4746 |                    It is copied up to the length of the buffer.
4747 |
4748 |   Return Values :  E_OK            - success
4749 |                    E_BAD_ARGUMENT  - invalid argument
4750 |                    E_SYSTEM_ERROR  - system error
4751 +--------------------------------------------------------------------------*/
4752 FORM_EXPORT(int)
set_field_buffer(FIELD * field,int buffer,const char * value)4753 set_field_buffer(FIELD *field, int buffer, const char *value)
4754 {
4755   FIELD_CELL *p;
4756   int res = E_OK;
4757   int i;
4758   int len;
4759 
4760 #if USE_WIDEC_SUPPORT
4761   FIELD_CELL *widevalue = NULL;
4762 #endif
4763 
4764   T((T_CALLED("set_field_buffer(%p,%d,%s)"), (void *)field, buffer, _nc_visbuf(value)));
4765 
4766   if (!field || !value || ((buffer < 0) || (buffer > field->nbuf)))
4767     RETURN(E_BAD_ARGUMENT);
4768 
4769   len = Buffer_Length(field);
4770 
4771   if (Growable(field))
4772     {
4773       /* for a growable field we must assume zero terminated strings, because
4774          somehow we have to detect the length of what should be copied.
4775        */
4776       int vlen = (int)strlen(value);
4777 
4778       if (vlen > len)
4779 	{
4780 	  if (!Field_Grown(field,
4781 			   (int)(1 + (vlen - len) / ((field->rows + field->nrow)
4782 						     * field->cols))))
4783 	    RETURN(E_SYSTEM_ERROR);
4784 
4785 #if !USE_WIDEC_SUPPORT
4786 	  len = vlen;
4787 #endif
4788 	}
4789     }
4790 
4791   p = Address_Of_Nth_Buffer(field, buffer);
4792 
4793 #if USE_WIDEC_SUPPORT
4794   /*
4795    * Use addstr's logic for converting a string to an array of cchar_t's.
4796    * There should be a better way, but this handles nonspacing characters
4797    * and other special cases that we really do not want to handle here.
4798    */
4799 #if NCURSES_EXT_FUNCS
4800   if (wresize(field->working, 1, Buffer_Length(field) + 1) == ERR)
4801 #endif
4802     {
4803       delwin(field->working);
4804       field->working = newpad(1, Buffer_Length(field) + 1);
4805     }
4806   len = Buffer_Length(field);
4807   wclear(field->working);
4808   (void)mvwaddstr(field->working, 0, 0, value);
4809 
4810   if ((widevalue = typeCalloc(FIELD_CELL, len + 1)) == NULL)
4811     {
4812       RETURN(E_SYSTEM_ERROR);
4813     }
4814   else
4815     {
4816       for (i = 0; i < field->drows; ++i)
4817 	{
4818 	  (void)mvwin_wchnstr(field->working, 0, (int)i * field->dcols,
4819 			      widevalue + ((int)i * field->dcols),
4820 			      field->dcols);
4821 	}
4822       for (i = 0; i < len; ++i)
4823 	{
4824 	  if (CharEq(myZEROS, widevalue[i]))
4825 	    {
4826 	      while (i < len)
4827 		p[i++] = myBLANK;
4828 	      break;
4829 	    }
4830 	  p[i] = widevalue[i];
4831 	}
4832       free(widevalue);
4833     }
4834 #else
4835   for (i = 0; i < len; ++i)
4836     {
4837       if (value[i] == '\0')
4838 	{
4839 	  while (i < len)
4840 	    p[i++] = myBLANK;
4841 	  break;
4842 	}
4843       p[i] = value[i];
4844     }
4845 #endif
4846 
4847   if (buffer == 0)
4848     {
4849       int syncres;
4850 
4851       if (((syncres = Synchronize_Field(field)) != E_OK) &&
4852 	  (res == E_OK))
4853 	res = syncres;
4854       if (((syncres = Synchronize_Linked_Fields(field)) != E_OK) &&
4855 	  (res == E_OK))
4856 	res = syncres;
4857     }
4858   RETURN(res);
4859 }
4860 
4861 /*---------------------------------------------------------------------------
4862 |   Facility      :  libnform
4863 |   Function      :  char *field_buffer(const FIELD *field,int buffer)
4864 |
4865 |   Description   :  Return the address of the buffer for the field.
4866 |
4867 |   Return Values :  Pointer to buffer or NULL if arguments were invalid.
4868 +--------------------------------------------------------------------------*/
4869 FORM_EXPORT(char *)
field_buffer(const FIELD * field,int buffer)4870 field_buffer(const FIELD *field, int buffer)
4871 {
4872   char *result = NULL;
4873 
4874   T((T_CALLED("field_buffer(%p,%d)"), (const void *)field, buffer));
4875 
4876   if (field && (buffer >= 0) && (buffer <= field->nbuf))
4877     {
4878 #if USE_WIDEC_SUPPORT
4879       FIELD_CELL *data = Address_Of_Nth_Buffer(field, buffer);
4880       size_t need = 0;
4881       int size = Buffer_Length(field);
4882       int n;
4883 
4884       /* determine the number of bytes needed to store the expanded string */
4885       for (n = 0; n < size; ++n)
4886 	{
4887 	  if (!isWidecExt(data[n]) && data[n].chars[0] != L'\0')
4888 	    {
4889 	      mbstate_t state;
4890 	      size_t next;
4891 
4892 	      init_mb(state);
4893 	      next = _nc_wcrtomb(NULL, data[n].chars[0], &state);
4894 	      if (next > 0)
4895 		need += next;
4896 	    }
4897 	}
4898 
4899       /* allocate a place to store the expanded string */
4900       if (field->expanded[buffer] != NULL)
4901 	free(field->expanded[buffer]);
4902       field->expanded[buffer] = typeMalloc(char, need + 1);
4903 
4904       /*
4905        * Expand the multibyte data.
4906        *
4907        * It may also be multi-column data.  In that case, the data for a row
4908        * may be null-padded to align to the dcols/drows layout (or it may
4909        * contain embedded wide-character extensions).  Change the null-padding
4910        * to blanks as needed.
4911        */
4912       if ((result = field->expanded[buffer]) != NULL)
4913 	{
4914 	  wclear(field->working);
4915 	  wmove(field->working, 0, 0);
4916 	  for (n = 0; n < size; ++n)
4917 	    {
4918 	      if (!isWidecExt(data[n]) && data[n].chars[0] != L'\0')
4919 		wadd_wch(field->working, &data[n]);
4920 	    }
4921 	  wmove(field->working, 0, 0);
4922 	  winnstr(field->working, result, (int)need);
4923 	}
4924 #else
4925       result = Address_Of_Nth_Buffer(field, buffer);
4926 #endif
4927     }
4928   returnPtr(result);
4929 }
4930 
4931 #if USE_WIDEC_SUPPORT
4932 
4933 /*---------------------------------------------------------------------------
4934 | Convert a multibyte string to a wide-character string.  The result must be
4935 | freed by the caller.
4936 +--------------------------------------------------------------------------*/
4937 FORM_EXPORT(wchar_t *)
_nc_Widen_String(char * source,int * lengthp)4938 _nc_Widen_String(char *source, int *lengthp)
4939 {
4940   wchar_t *result = NULL;
4941   wchar_t wch = 0;
4942   size_t given = strlen(source);
4943   size_t tries;
4944   int pass;
4945   int status;
4946 
4947 #ifndef state_unused
4948   mbstate_t state;
4949 #endif
4950 
4951   for (pass = 0; pass < 2; ++pass)
4952     {
4953       unsigned need = 0;
4954       size_t passed = 0;
4955 
4956       while (passed < given)
4957 	{
4958 	  bool found = FALSE;
4959 
4960 	  for (tries = 1, status = 0; tries <= (given - passed); ++tries)
4961 	    {
4962 	      int save = source[passed + tries];
4963 
4964 	      source[passed + tries] = 0;
4965 	      reset_mbytes(state);
4966 	      status = check_mbytes(wch, source + passed, tries, state);
4967 	      source[passed + tries] = (char)save;
4968 
4969 	      if (status > 0)
4970 		{
4971 		  found = TRUE;
4972 		  break;
4973 		}
4974 	    }
4975 	  if (found)
4976 	    {
4977 	      if (pass)
4978 		{
4979 		  result[need] = wch;
4980 		}
4981 	      passed += (size_t)status;
4982 	      ++need;
4983 	    }
4984 	  else
4985 	    {
4986 	      if (pass)
4987 		{
4988 		  result[need] = (wchar_t)source[passed];
4989 		}
4990 	      ++need;
4991 	      ++passed;
4992 	    }
4993 	}
4994 
4995       if (!pass)
4996 	{
4997 	  if (!need)
4998 	    break;
4999 	  result = typeCalloc(wchar_t, need);
5000 
5001 	  *lengthp = (int)need;
5002 	  if (result == NULL)
5003 	    break;
5004 	}
5005     }
5006 
5007   return result;
5008 }
5009 #endif
5010 
5011 /* frm_driver.c ends here */
5012