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