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