internals.c revision 1.21 1 /* $NetBSD: internals.c,v 1.21 2001/06/28 11:38:19 blymn Exp $ */
2
3 /*-
4 * Copyright (c) 1998-1999 Brett Lymn
5 * (blymn (at) baea.com.au, brett_lymn (at) yahoo.com.au)
6 * All rights reserved.
7 *
8 * This code has been donated to The NetBSD Foundation by the Author.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 *
30 */
31
32 #include <limits.h>
33 #include <ctype.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <strings.h>
37 #include <assert.h>
38 #include "internals.h"
39 #include "form.h"
40
41 #ifdef DEBUG
42 /*
43 * file handle to write debug info to, this will be initialised when
44 * the form is first posted.
45 */
46 FILE *dbg = NULL;
47 #endif
48
49 /* define our own min function - this is not generic but will do here
50 * (don't believe me? think about what value you would get
51 * from min(x++, y++)
52 */
53 #define min(a,b) (((a) > (b))? (b) : (a))
54
55 /* for the line joining function... */
56 #define JOIN_NEXT 1
57 #define JOIN_NEXT_NW 2 /* next join, don't wrap the joined line */
58 #define JOIN_PREV 3
59 #define JOIN_PREV_NW 4 /* previous join, don't wrap the joined line */
60
61 /* for the bump_lines function... */
62 #define _FORMI_USE_CURRENT -1 /* indicates current cursor pos to be used */
63
64 static void
65 _formi_do_char_validation(FIELD *field, FIELDTYPE *type, char c, int *ret_val);
66 static void
67 _formi_do_validation(FIELD *field, FIELDTYPE *type, int *ret_val);
68 static int
69 _formi_join_line(FIELD *field, unsigned int pos, int direction);
70 void
71 _formi_hscroll_back(FIELD *field, unsigned int amt);
72 void
73 _formi_hscroll_fwd(FIELD *field, unsigned int amt);
74 static void
75 _formi_scroll_back(FIELD *field, unsigned int amt);
76 static void
77 _formi_scroll_fwd(FIELD *field, unsigned int amt);
78 static int
79 find_sow(char *str, unsigned int offset);
80 static int
81 find_cur_line(FIELD *cur, unsigned pos);
82 static int
83 split_line(FIELD *field, unsigned pos);
84 static void
85 bump_lines(FIELD *field, int pos, int amt, bool do_len);
86 static bool
87 check_dynamic_size(FIELD *field);
88
89
90 /*
91 * Open the debug file if it is not already open....
92 */
93 #ifdef DEBUG
94 int
95 _formi_create_dbg_file(void)
96 {
97 if (dbg == NULL) {
98 dbg = fopen("___form_dbg.out", "w");
99 if (dbg == NULL) {
100 fprintf(stderr, "Cannot open debug file!\n");
101 return E_SYSTEM_ERROR;
102 }
103 }
104
105 return E_OK;
106 }
107 #endif
108
109 /*
110 * Bump the lines array elements in the given field by the given amount.
111 * The row to start acting on can either be inferred from the given position
112 * or if the special value _FORMI_USE_CURRENT is set then the row will be
113 * the row the cursor is currently on.
114 */
115 static void
116 bump_lines(FIELD *field, int pos, int amt, bool do_len)
117 {
118 int i, row;
119 #ifdef DEBUG
120 int dbg_ok = FALSE;
121 #endif
122
123 if (pos == _FORMI_USE_CURRENT)
124 row = field->start_line + field->cursor_ypos;
125 else
126 row = find_cur_line(field, (unsigned) pos);
127
128 #ifdef DEBUG
129 if (_formi_create_dbg_file() == E_OK) {
130 dbg_ok = TRUE;
131 fprintf(dbg, "bump_lines: bump starting at row %d\n", row);
132 fprintf(dbg,
133 "bump_lines: len from %d to %d, end from %d to %d\n",
134 field->lines[row].length,
135 field->lines[row].length + amt,
136 field->lines[row].end, field->lines[row].end + amt);
137 }
138 #endif
139
140 if (((int)field->lines[row].length + amt) < 0) {
141 field->lines[row].length = 0;
142 field->lines[row].end = 0;
143 } else {
144 if (do_len == TRUE)
145 field->lines[row].length += amt;
146 }
147
148 if (field->lines[row].length > 1)
149 field->lines[row].end += amt;
150 else
151 field->lines[row].end = field->lines[row].start;
152
153 for (i = row + 1; i < field->row_count; i++) {
154 #ifdef DEBUG
155 if (dbg_ok) {
156 fprintf(dbg,
157 "bump_lines: row %d: len from %d to %d, end from %d to %d\n",
158 i, field->lines[i].start,
159 field->lines[i].start + amt,
160 field->lines[i].end,
161 field->lines[i].end + amt);
162 }
163 fflush(dbg);
164 #endif
165 field->lines[i].start += amt;
166 field->lines[i].end += amt;
167 }
168 }
169
170 /*
171 * Check the sizing of the dynamic field, if the maximum size is set then
172 * check that the number of rows or columns does not exceed the set
173 * maximum. The decision to check the rows or columns is made on the basis
174 * of how many rows are in the field - one row means the max applies to
175 * the number of columns otherwise it applies to the number of rows. If
176 * the row/column count is less than the maximum then return TRUE.
177 *
178 */
179 bool
180 check_dynamic_size(FIELD *field)
181 {
182 if (field->max == 0) /* unlimited */
183 return TRUE;
184
185 if (field->rows == 1) {
186 if (field->buffers[0].length >= field->max)
187 return FALSE;
188 else
189 return TRUE;
190 } else {
191 if (field->row_count > field->max)
192 return FALSE;
193 else
194 return TRUE;
195 }
196 }
197
198 /*
199 * Set the form's current field to the first valid field on the page.
200 * Assume the fields have been sorted and stitched.
201 */
202 int
203 _formi_pos_first_field(FORM *form)
204 {
205 FIELD *cur;
206 int old_page;
207
208 old_page = form->page;
209
210 /* scan forward for an active page....*/
211 while (form->page_starts[form->page].in_use == 0) {
212 form->page++;
213 if (form->page > form->max_page) {
214 form->page = old_page;
215 return E_REQUEST_DENIED;
216 }
217 }
218
219 /* then scan for a field we can use */
220 cur = form->fields[form->page_starts[form->page].first];
221 while ((cur->opts & (O_VISIBLE | O_ACTIVE))
222 != (O_VISIBLE | O_ACTIVE)) {
223 cur = CIRCLEQ_NEXT(cur, glue);
224 if (cur == (void *) &form->sorted_fields) {
225 form->page = old_page;
226 return E_REQUEST_DENIED;
227 }
228 }
229
230 form->cur_field = cur->index;
231 return E_OK;
232 }
233
234 /*
235 * Set the field to the next active and visible field, the fields are
236 * traversed in index order in the direction given. If the parameter
237 * use_sorted is TRUE then the sorted field list will be traversed instead
238 * of using the field index.
239 */
240 int
241 _formi_pos_new_field(FORM *form, unsigned direction, unsigned use_sorted)
242 {
243 FIELD *cur;
244 int i;
245
246 i = form->cur_field;
247 cur = form->fields[i];
248
249 do {
250 if (direction == _FORMI_FORWARD) {
251 if (use_sorted == TRUE) {
252 if ((form->wrap == FALSE) &&
253 (cur == CIRCLEQ_LAST(&form->sorted_fields)))
254 return E_REQUEST_DENIED;
255 cur = CIRCLEQ_NEXT(cur, glue);
256 i = cur->index;
257 } else {
258 if ((form->wrap == FALSE) &&
259 ((i + 1) >= form->field_count))
260 return E_REQUEST_DENIED;
261 i++;
262 if (i >= form->field_count)
263 i = 0;
264 }
265 } else {
266 if (use_sorted == TRUE) {
267 if ((form->wrap == FALSE) &&
268 (cur == CIRCLEQ_FIRST(&form->sorted_fields)))
269 return E_REQUEST_DENIED;
270 cur = CIRCLEQ_PREV(cur, glue);
271 i = cur->index;
272 } else {
273 if ((form->wrap == FALSE) && (i <= 0))
274 return E_REQUEST_DENIED;
275 i--;
276 if (i < 0)
277 i = form->field_count - 1;
278 }
279 }
280
281 if ((form->fields[i]->opts & (O_VISIBLE | O_ACTIVE))
282 == (O_VISIBLE | O_ACTIVE)) {
283 form->cur_field = i;
284 return E_OK;
285 }
286 }
287 while (i != form->cur_field);
288
289 return E_REQUEST_DENIED;
290 }
291
292 /*
293 * Find the line in a field that the cursor is currently on.
294 */
295 static int
296 find_cur_line(FIELD *cur, unsigned pos)
297 {
298 unsigned row;
299
300 /* first check if pos is at the end of the string, if this
301 * is true then just return the last row since the pos may
302 * not have been added to the lines array yet.
303 */
304 if (pos == (cur->buffers[0].length - 1))
305 return (cur->row_count - 1);
306
307 for (row = 0; row < cur->row_count; row++) {
308 if ((pos >= cur->lines[row].start)
309 && (pos <= cur->lines[row].end))
310 return row;
311 }
312
313 #ifdef DEBUG
314 /* barf if we get here, this should not be possible */
315 assert((row != row));
316 #endif
317 return 0;
318 }
319
320
321 /*
322 * Word wrap the contents of the field's buffer 0 if this is allowed.
323 * If the wrap is successful, that is, the row count nor the buffer
324 * size is exceeded then the function will return E_OK, otherwise it
325 * will return E_REQUEST_DENIED.
326 */
327 int
328 _formi_wrap_field(FIELD *field, unsigned int loc)
329 {
330 char *str;
331 int width, row, start_row;
332 unsigned int pos;
333
334 str = field->buffers[0].string;
335
336 /* Don't bother if the field string is too short. */
337 if (field->buffers[0].length < field->cols)
338 return E_OK;
339
340 if ((field->opts & O_STATIC) == O_STATIC) {
341 if ((field->rows + field->nrows) == 1) {
342 return E_OK; /* cannot wrap a single line */
343 }
344 width = field->cols;
345 } else {
346 /* if we are limited to one line then don't try to wrap */
347 if ((field->drows + field->nrows) == 1) {
348 return E_OK;
349 }
350
351 /*
352 * hueristic - if a dynamic field has more than one line
353 * on the screen then the field grows rows, otherwise
354 * it grows columns, effectively a single line field.
355 * This is documented AT&T behaviour.
356 */
357 if (field->rows > 1) {
358 width = field->cols;
359 } else {
360 return E_OK;
361 }
362 }
363
364 start_row = find_cur_line(field, loc);
365
366 /* if we are not at the top of the field then back up one
367 * row because we may be able to merge the current row into
368 * the one above.
369 */
370 if (start_row > 0)
371 start_row--;
372
373 for (row = start_row; row < field->row_count; row++) {
374 AGAIN:
375 pos = field->lines[row].end;
376 if (field->lines[row].length < width) {
377 /* line may be too short, try joining some lines */
378
379 if ((((int) field->row_count) - 1) == row) {
380 /* if this is the last row then don't
381 * wrap
382 */
383 continue;
384 }
385
386 if (_formi_join_line(field, (unsigned int) pos,
387 JOIN_NEXT_NW) == E_OK) {
388 goto AGAIN;
389 } else
390 break;
391 } else {
392 /* line is too long, split it - maybe */
393
394 /* first check if we have not run out of room */
395 if ((field->opts & O_STATIC) == O_STATIC) {
396 /* check static field */
397 if ((field->rows + field->nrows - 1) == row)
398 return E_REQUEST_DENIED;
399 } else {
400 /* check dynamic field */
401 if ((field->max != 0)
402 && ((field->max - 1) == row))
403 return E_REQUEST_DENIED;
404 }
405
406 /* split on first whitespace before current word */
407 pos = width + field->lines[row].start - 1;
408 if (pos >= field->buffers[0].length)
409 pos = field->buffers[0].length - 1;
410
411 if ((!isblank(str[pos])) &&
412 ((field->opts & O_WRAP) == O_WRAP)) {
413 if (!isblank(str[pos - 1]))
414 pos = find_sow(str,
415 (unsigned int) pos);
416 /*
417 * If we cannot split the line then return
418 * NO_ROOM so the driver can tell that it
419 * should not autoskip (if that is enabled)
420 */
421 if ((pos == 0) || (!isblank(str[pos - 1]))
422 || ((pos <= field->lines[row].start)
423 && (field->buffers[0].length
424 >= (width - 1
425 + field->lines[row].start)))) {
426 return E_NO_ROOM;
427 }
428 }
429
430 /* if we are at the end of the string and it has
431 * a trailing blank, don't wrap the blank.
432 */
433 if ((pos == field->buffers[0].length - 1) &&
434 (isblank(str[pos])))
435 continue;
436
437 /*
438 * otherwise, if we are still sitting on a
439 * blank but not at the end of the line
440 * move forward one char so the blank
441 * is on the line boundary.
442 */
443 if (isblank(str[pos]))
444 pos++;
445
446 if (split_line(field, pos) != E_OK) {
447 return E_REQUEST_DENIED;
448 }
449 }
450 }
451
452 return E_OK;
453 }
454
455 /*
456 * Join the two lines that surround the location pos, the type
457 * variable indicates the direction of the join, JOIN_NEXT will join
458 * the next line to the current line, JOIN_PREV will join the current
459 * line to the previous line, the new lines will be wrapped unless the
460 * _NW versions of the directions are used. Returns E_OK if the join
461 * was successful or E_REQUEST_DENIED if the join cannot happen.
462 */
463 static int
464 _formi_join_line(FIELD *field, unsigned int pos, int direction)
465 {
466 unsigned int row, i;
467 int old_alloced, old_row_count;
468 struct _formi_field_lines *saved;
469 #ifdef DEBUG
470 int dbg_ok = FALSE;
471
472 if (_formi_create_dbg_file() == E_OK) {
473 dbg_ok = TRUE;
474 }
475 #endif
476
477 if ((saved = (struct _formi_field_lines *)
478 malloc(field->lines_alloced * sizeof(struct _formi_field_lines)))
479 == NULL)
480 return E_REQUEST_DENIED;
481
482 bcopy(field->lines, saved,
483 field->row_count * sizeof(struct _formi_field_lines));
484 old_alloced = field->lines_alloced;
485 old_row_count = field->row_count;
486
487 row = find_cur_line(field, pos);
488
489 #ifdef DEBUG
490 if (dbg_ok == TRUE) {
491 fprintf(dbg, "join_line: working on row %d, row_count = %d\n",
492 row, field->row_count);
493 }
494 #endif
495
496 if ((direction == JOIN_NEXT) || (direction == JOIN_NEXT_NW)) {
497 /* see if there is another line following... */
498 if (row == (field->row_count - 1)) {
499 free(saved);
500 return E_REQUEST_DENIED;
501 }
502
503 #ifdef DEBUG
504 if (dbg_ok == TRUE) {
505 fprintf(dbg,
506 "join_line: join_next before end = %d, length = %d",
507 field->lines[row].end,
508 field->lines[row].length);
509 fprintf(dbg,
510 " :: next row end = %d, length = %d\n",
511 field->lines[row + 1].end,
512 field->lines[row + 1].length);
513 }
514 #endif
515
516 field->lines[row].end = field->lines[row + 1].end;
517 field->lines[row].length += field->lines[row + 1].length;
518 /* shift all the remaining lines up.... */
519 for (i = row + 2; i < field->row_count; i++)
520 field->lines[i - 1] = field->lines[i];
521 } else {
522 if ((pos == 0) || (row == 0)) {
523 free(saved);
524 return E_REQUEST_DENIED;
525 }
526
527 #ifdef DEBUG
528 if (dbg_ok == TRUE) {
529 fprintf(dbg,
530 "join_line: join_prev before end = %d, length = %d",
531 field->lines[row].end,
532 field->lines[row].length);
533 fprintf(dbg,
534 " :: prev row end = %d, length = %d\n",
535 field->lines[row - 1].end,
536 field->lines[row - 1].length);
537 }
538 #endif
539
540 field->lines[row - 1].end = field->lines[row].end;
541 field->lines[row - 1].length += field->lines[row].length;
542 /* shift all the remaining lines up */
543 for (i = row + 1; i < field->row_count; i++)
544 field->lines[i - 1] = field->lines[i];
545 }
546
547 #ifdef DEBUG
548 if (dbg_ok == TRUE) {
549 fprintf(dbg,
550 "join_line: exit end = %d, length = %d\n",
551 field->lines[row].end, field->lines[row].length);
552 }
553 #endif
554
555 field->row_count--;
556
557 /* wrap the field if required, if this fails undo the change */
558 if ((direction == JOIN_NEXT) || (direction == JOIN_PREV)) {
559 if (_formi_wrap_field(field, (unsigned int) pos) != E_OK) {
560 free(field->lines);
561 field->lines = saved;
562 field->lines_alloced = old_alloced;
563 field->row_count = old_row_count;
564 return E_REQUEST_DENIED;
565 }
566 }
567
568 free(saved);
569 return E_OK;
570 }
571
572 /*
573 * Split the line at the given position, if possible
574 */
575 static int
576 split_line(FIELD *field, unsigned pos)
577 {
578 struct _formi_field_lines *new_lines;
579 unsigned int row, i;
580 #ifdef DEBUG
581 short dbg_ok = FALSE;
582 #endif
583
584 if (pos == 0)
585 return E_REQUEST_DENIED;
586
587 #ifdef DEBUG
588 if (_formi_create_dbg_file() == E_OK) {
589 fprintf(dbg, "split_line: splitting line at %d\n", pos);
590 dbg_ok = TRUE;
591 }
592 #endif
593
594 if ((field->row_count + 1) > field->lines_alloced) {
595 if ((new_lines = (struct _formi_field_lines *)
596 realloc(field->lines, (field->row_count + 1)
597 * sizeof(struct _formi_field_lines))) == NULL)
598 return E_SYSTEM_ERROR;
599 field->lines = new_lines;
600 field->lines_alloced++;
601 }
602
603 row = find_cur_line(field, pos);
604 #ifdef DEBUG
605 if (dbg_ok == TRUE) {
606 fprintf(dbg,
607 "split_line: enter: lines[%d].end = %d, lines[%d].length = %d\n",
608 row, field->lines[row].end, row,
609 field->lines[row].length);
610 }
611
612 assert(((field->lines[row].end < INT_MAX) &&
613 (field->lines[row].length < INT_MAX) &&
614 (field->lines[row].length > 0)));
615
616 #endif
617
618 /* if asked to split right where the line already starts then
619 * just return - nothing to do.
620 */
621 if (field->lines[row].start == pos)
622 return E_OK;
623
624 for (i = field->row_count - 1; i > row; i--) {
625 field->lines[i + 1] = field->lines[i];
626 }
627
628 field->lines[row + 1].end = field->lines[row].end;
629 field->lines[row].end = pos - 1;
630 field->lines[row].length = pos - field->lines[row].start;
631 field->lines[row + 1].start = pos;
632 field->lines[row + 1].length = field->lines[row + 1].end
633 - field->lines[row + 1].start + 1;
634
635 #ifdef DEBUG
636 assert(((field->lines[row + 1].end < INT_MAX) &&
637 (field->lines[row].end < INT_MAX) &&
638 (field->lines[row].length < INT_MAX) &&
639 (field->lines[row + 1].start < INT_MAX) &&
640 (field->lines[row + 1].length < INT_MAX) &&
641 (field->lines[row].length > 0) &&
642 (field->lines[row + 1].length > 0)));
643
644 if (dbg_ok == TRUE) {
645 fprintf(dbg,
646 "split_line: exit: lines[%d].end = %d, lines[%d].length = %d, ",
647 row, field->lines[row].end, row,
648 field->lines[row].length);
649 fprintf(dbg, "lines[%d].start = %d, lines[%d].end = %d, ",
650 row + 1, field->lines[row + 1].start, row + 1,
651 field->lines[row + 1].end);
652 fprintf(dbg, "lines[%d].length = %d, row_count = %d\n",
653 row + 1, field->lines[row + 1].length,
654 field->row_count + 1);
655 }
656 #endif
657
658 field->row_count++;
659
660 #ifdef DEBUG
661 if (dbg_ok == TRUE) {
662 bump_lines(field, 0, 0, FALSE); /* will report line data for us */
663 }
664 #endif
665
666 return E_OK;
667 }
668
669 /*
670 * skip the blanks in the given string, start at the index start and
671 * continue forward until either the end of the string or a non-blank
672 * character is found. Return the index of either the end of the string or
673 * the first non-blank character.
674 */
675 unsigned
676 _formi_skip_blanks(char *string, unsigned int start)
677 {
678 unsigned int i;
679
680 i = start;
681
682 while ((string[i] != '\0') && isblank(string[i]))
683 i++;
684
685 return i;
686 }
687
688 /*
689 * Return the index of the top left most field of the two given fields.
690 */
691 static int
692 _formi_top_left(FORM *form, int a, int b)
693 {
694 /* lower row numbers always win here.... */
695 if (form->fields[a]->form_row < form->fields[b]->form_row)
696 return a;
697
698 if (form->fields[a]->form_row > form->fields[b]->form_row)
699 return b;
700
701 /* rows must be equal, check columns */
702 if (form->fields[a]->form_col < form->fields[b]->form_col)
703 return a;
704
705 if (form->fields[a]->form_col > form->fields[b]->form_col)
706 return b;
707
708 /* if we get here fields must be in exactly the same place, punt */
709 return a;
710 }
711
712 /*
713 * Return the index to the field that is the bottom-right-most of the
714 * two given fields.
715 */
716 static int
717 _formi_bottom_right(FORM *form, int a, int b)
718 {
719 /* check the rows first, biggest row wins */
720 if (form->fields[a]->form_row > form->fields[b]->form_row)
721 return a;
722 if (form->fields[a]->form_row < form->fields[b]->form_row)
723 return b;
724
725 /* rows must be equal, check cols, biggest wins */
726 if (form->fields[a]->form_col > form->fields[b]->form_col)
727 return a;
728 if (form->fields[a]->form_col < form->fields[b]->form_col)
729 return b;
730
731 /* fields in the same place, punt */
732 return a;
733 }
734
735 /*
736 * Find the end of the current word in the string str, starting at
737 * offset - the end includes any trailing whitespace. If the end of
738 * the string is found before a new word then just return the offset
739 * to the end of the string.
740 */
741 static int
742 find_eow(char *str, unsigned int offset)
743 {
744 int start;
745
746 start = offset;
747 /* first skip any non-whitespace */
748 while ((str[start] != '\0') && !isblank(str[start]))
749 start++;
750
751 /* see if we hit the end of the string */
752 if (str[start] == '\0')
753 return start;
754
755 /* otherwise skip the whitespace.... */
756 while ((str[start] != '\0') && isblank(str[start]))
757 start++;
758
759 return start;
760 }
761
762 /*
763 * Find the beginning of the current word in the string str, starting
764 * at offset.
765 */
766 static int
767 find_sow(char *str, unsigned int offset)
768 {
769 int start;
770
771 start = offset;
772
773 if (start > 0) {
774 if (isblank(str[start]) || isblank(str[start - 1])) {
775 if (isblank(str[start - 1]))
776 start--;
777 /* skip the whitespace.... */
778 while ((start >= 0) && isblank(str[start]))
779 start--;
780 }
781 }
782
783 /* see if we hit the start of the string */
784 if (start < 0)
785 return 0;
786
787 /* now skip any non-whitespace */
788 while ((start >= 0) && !isblank(str[start]))
789 start--;
790
791 if (start > 0)
792 start++; /* last loop has us pointing at a space, adjust */
793
794 if (start < 0)
795 start = 0;
796
797 return start;
798 }
799
800 /*
801 * Scroll the field forward the given number of lines.
802 */
803 static void
804 _formi_scroll_fwd(FIELD *field, unsigned int amt)
805 {
806 /* check if we have lines to scroll */
807 if (field->row_count < (field->start_line + field->rows))
808 return;
809
810 field->start_line += min(amt,
811 field->row_count - field->start_line
812 - field->rows);
813 }
814
815 /*
816 * Scroll the field backward the given number of lines.
817 */
818 static void
819 _formi_scroll_back(FIELD *field, unsigned int amt)
820 {
821 if (field->start_line == 0)
822 return;
823
824 field->start_line -= min(field->start_line, amt);
825 }
826
827 /*
828 * Scroll the field forward the given number of characters.
829 */
830 void
831 _formi_hscroll_fwd(FIELD *field, int unsigned amt)
832 {
833 field->start_char += min(amt,
834 field->lines[field->start_line + field->cursor_ypos].end);
835 }
836
837 /*
838 * Scroll the field backward the given number of characters.
839 */
840 void
841 _formi_hscroll_back(FIELD *field, unsigned int amt)
842 {
843 field->start_char -= min(field->start_char, amt);
844 }
845
846 /*
847 * Find the different pages in the form fields and assign the form
848 * page_starts array with the information to find them.
849 */
850 int
851 _formi_find_pages(FORM *form)
852 {
853 int i, cur_page = 0;
854
855 if ((form->page_starts = (_FORMI_PAGE_START *)
856 malloc((form->max_page + 1) * sizeof(_FORMI_PAGE_START))) == NULL)
857 return E_SYSTEM_ERROR;
858
859 /* initialise the page starts array */
860 memset(form->page_starts, 0,
861 (form->max_page + 1) * sizeof(_FORMI_PAGE_START));
862
863 for (i =0; i < form->field_count; i++) {
864 if (form->fields[i]->page_break == 1)
865 cur_page++;
866 if (form->page_starts[cur_page].in_use == 0) {
867 form->page_starts[cur_page].in_use = 1;
868 form->page_starts[cur_page].first = i;
869 form->page_starts[cur_page].last = i;
870 form->page_starts[cur_page].top_left = i;
871 form->page_starts[cur_page].bottom_right = i;
872 } else {
873 form->page_starts[cur_page].last = i;
874 form->page_starts[cur_page].top_left =
875 _formi_top_left(form,
876 form->page_starts[cur_page].top_left,
877 i);
878 form->page_starts[cur_page].bottom_right =
879 _formi_bottom_right(form,
880 form->page_starts[cur_page].bottom_right,
881 i);
882 }
883 }
884
885 return E_OK;
886 }
887
888 /*
889 * Completely redraw the field of the given form.
890 */
891 void
892 _formi_redraw_field(FORM *form, int field)
893 {
894 unsigned int pre, post, flen, slen, i, row, start, last_row;
895 char *str;
896 FIELD *cur;
897 #ifdef DEBUG
898 char buffer[100];
899 #endif
900
901 cur = form->fields[field];
902 str = cur->buffers[0].string;
903 flen = cur->cols;
904 slen = 0;
905 start = 0;
906
907 if ((cur->row_count - cur->start_line) < cur->rows)
908 last_row = cur->row_count;
909 else
910 last_row = cur->start_line + cur->rows;
911
912 for (row = cur->start_line; row < last_row; row++) {
913 wmove(form->scrwin,
914 (int) (cur->form_row + row - cur->start_line),
915 (int) cur->form_col);
916 start = cur->lines[row].start;
917 slen = cur->lines[row].length;
918
919 if ((cur->opts & O_STATIC) == O_STATIC) {
920 switch (cur->justification) {
921 case JUSTIFY_RIGHT:
922 post = 0;
923 if (flen < slen)
924 pre = 0;
925 else
926 pre = flen - slen;
927 break;
928
929 case JUSTIFY_CENTER:
930 if (flen < slen) {
931 pre = 0;
932 post = 0;
933 } else {
934 pre = flen - slen;
935 post = pre = pre / 2;
936 /* get padding right if
937 centring is not even */
938 if ((post + pre + slen) < flen)
939 post++;
940 }
941 break;
942
943 case NO_JUSTIFICATION:
944 case JUSTIFY_LEFT:
945 default:
946 pre = 0;
947 if (flen <= slen)
948 post = 0;
949 else {
950 post = flen - slen;
951 if (post > flen)
952 post = flen;
953 }
954 break;
955 }
956 } else {
957 /* dynamic fields are not justified */
958 pre = 0;
959 if (flen <= slen)
960 post = 0;
961 else {
962 post = flen - slen;
963 if (post > flen)
964 post = flen;
965 }
966
967 /* but they do scroll.... */
968
969 if (pre > cur->start_char - start)
970 pre = pre - cur->start_char + start;
971 else
972 pre = 0;
973
974 if (slen > cur->start_char) {
975 slen -= cur->start_char;
976 post += cur->start_char;
977 if (post > flen)
978 post = flen;
979 } else {
980 slen = 0;
981 post = flen - pre;
982 }
983 }
984
985 if (form->cur_field == field)
986 wattrset(form->scrwin, cur->fore);
987 else
988 wattrset(form->scrwin, cur->back);
989
990 #ifdef DEBUG
991 if (_formi_create_dbg_file() == E_OK) {
992 fprintf(dbg,
993 "redraw_field: start=%d, pre=%d, slen=%d, flen=%d, post=%d, start_char=%d\n",
994 start, pre, slen, flen, post, cur->start_char);
995 if (str != NULL) {
996 strncpy(buffer,
997 &str[cur->start_char
998 + cur->lines[row].start], flen);
999 } else {
1000 strcpy(buffer, "(null)");
1001 }
1002 buffer[flen] = '\0';
1003 fprintf(dbg, "redraw_field: %s\n", buffer);
1004 }
1005 #endif
1006
1007 for (i = start + cur->start_char; i < pre; i++)
1008 waddch(form->scrwin, cur->pad);
1009
1010 #ifdef DEBUG
1011 fprintf(dbg, "redraw_field: will add %d chars\n",
1012 min(slen, flen));
1013 #endif
1014 for (i = 0; i < min(slen, flen); i++)
1015 {
1016 #ifdef DEBUG
1017 fprintf(dbg, "adding char str[%d]=%c\n",
1018 i + cur->start_char + cur->lines[row].start,
1019 str[i + cur->start_char
1020 + cur->lines[row].start]);
1021 #endif
1022 if (((cur->opts & O_PUBLIC) != O_PUBLIC)) {
1023 waddch(form->scrwin, cur->pad);
1024 } else if ((cur->opts & O_VISIBLE) == O_VISIBLE) {
1025 waddch(form->scrwin, str[i + cur->start_char
1026 + cur->lines[row].start]);
1027 } else {
1028 waddch(form->scrwin, ' ');
1029 }
1030 }
1031
1032 for (i = 0; i < post; i++)
1033 waddch(form->scrwin, cur->pad);
1034 }
1035
1036 for (row = cur->row_count - cur->start_line; row < cur->rows; row++) {
1037 wmove(form->scrwin, (int) (cur->form_row + row),
1038 (int) cur->form_col);
1039 for (i = 0; i < cur->cols; i++) {
1040 waddch(form->scrwin, cur->pad);
1041 }
1042 }
1043
1044 return;
1045 }
1046
1047 /*
1048 * Display the fields attached to the form that are on the current page
1049 * on the screen.
1050 *
1051 */
1052 int
1053 _formi_draw_page(FORM *form)
1054 {
1055 int i;
1056
1057 if (form->page_starts[form->page].in_use == 0)
1058 return E_BAD_ARGUMENT;
1059
1060 wclear(form->scrwin);
1061
1062 for (i = form->page_starts[form->page].first;
1063 i <= form->page_starts[form->page].last; i++)
1064 _formi_redraw_field(form, i);
1065
1066 return E_OK;
1067 }
1068
1069 /*
1070 * Add the character c at the position pos in buffer 0 of the given field
1071 */
1072 int
1073 _formi_add_char(FIELD *field, unsigned int pos, char c)
1074 {
1075 char *new, old_c;
1076 unsigned int new_size;
1077 int status;
1078
1079 /*
1080 * If buffer has not had a string before, set it to a blank
1081 * string. Everything should flow from there....
1082 */
1083 if (field->buffers[0].string == NULL) {
1084 set_field_buffer(field, 0, "");
1085 }
1086
1087 if (_formi_validate_char(field, c) != E_OK) {
1088 #ifdef DEBUG
1089 fprintf(dbg, "add_char: char %c failed char validation\n", c);
1090 #endif
1091 return E_INVALID_FIELD;
1092 }
1093
1094 #ifdef DEBUG
1095 fprintf(dbg, "add_char: pos=%d, char=%c\n", pos, c);
1096 fprintf(dbg,
1097 "add_char enter: xpos=%d, start=%d, length=%d(%d), allocated=%d\n",
1098 field->cursor_xpos, field->start_char,
1099 field->buffers[0].length, strlen(field->buffers[0].string),
1100 field->buffers[0].allocated);
1101 fprintf(dbg, "add_char enter: %s\n", field->buffers[0].string);
1102 fprintf(dbg, "add_char enter: buf0_status=%d\n", field->buf0_status);
1103 #endif
1104 if (((field->opts & O_BLANK) == O_BLANK) &&
1105 (field->buf0_status == FALSE) &&
1106 ((field->cursor_xpos + field->start_char) == 0)) {
1107 field->buffers[0].length = 0;
1108 field->buffers[0].string[0] = '\0';
1109 pos = 0;
1110 field->start_char = 0;
1111 field->start_line = 0;
1112 field->row_count = 1;
1113 field->cursor_xpos = 0;
1114 field->cursor_ypos = 0;
1115 field->lines[0].start = 0;
1116 field->lines[0].end = 0;
1117 field->lines[0].length = 0;
1118 }
1119
1120
1121 if ((field->overlay == 0)
1122 || ((field->overlay == 1) && (pos >= field->buffers[0].length))) {
1123 /* first check if the field can have more chars...*/
1124 if ((((field->opts & O_STATIC) == O_STATIC) &&
1125 (field->buffers[0].length >= (field->cols * field->rows))) ||
1126 (((field->opts & O_STATIC) != O_STATIC) &&
1127 (check_dynamic_size(field) == FALSE)))
1128 return E_REQUEST_DENIED;
1129
1130 if (field->buffers[0].length + 1
1131 >= field->buffers[0].allocated) {
1132 new_size = field->buffers[0].allocated + 64
1133 - (field->buffers[0].allocated % 64);
1134 if ((new = (char *) realloc(field->buffers[0].string,
1135 new_size )) == NULL)
1136 return E_SYSTEM_ERROR;
1137 field->buffers[0].allocated = new_size;
1138 field->buffers[0].string = new;
1139 }
1140 }
1141
1142 if ((field->overlay == 0) && (field->buffers[0].length > pos)) {
1143 bcopy(&field->buffers[0].string[pos],
1144 &field->buffers[0].string[pos + 1],
1145 field->buffers[0].length - pos + 1);
1146 }
1147
1148 old_c = field->buffers[0].string[pos];
1149 field->buffers[0].string[pos] = c;
1150 if (pos >= field->buffers[0].length) {
1151 /* make sure the string is terminated if we are at the
1152 * end of the string, the terminator would be missing
1153 * if we are are at the end of the field.
1154 */
1155 field->buffers[0].string[pos + 1] = '\0';
1156 }
1157
1158 /* only increment the length if we are inserting characters
1159 * OR if we are at the end of the field in overlay mode.
1160 */
1161 if ((field->overlay == 0)
1162 || ((field->overlay == 1) && (pos >= field->buffers[0].length))) {
1163 field->buffers[0].length++;
1164 bump_lines(field, (int) pos, 1, TRUE);
1165 }
1166
1167
1168 /* wrap the field, if needed */
1169 status = _formi_wrap_field(field, pos);
1170
1171 /*
1172 * check the wrap worked or that we have not exceeded the
1173 * max field size - this can happen if the field is re-wrapped
1174 * and the row count is increased past the set limit.
1175 */
1176 if ((status != E_OK) || (check_dynamic_size(field) == FALSE)) {
1177 if ((field->overlay == 0)
1178 || ((field->overlay == 1)
1179 && (pos >= field->buffers[0].length))) {
1180 /*
1181 * wrap failed for some reason, back out the
1182 * char insert
1183 */
1184 bcopy(&field->buffers[0].string[pos + 1],
1185 &field->buffers[0].string[pos],
1186 field->buffers[0].length - pos);
1187 field->buffers[0].length--;
1188 bump_lines(field, (int) pos, -1, TRUE);
1189 } else if (field->overlay == 1) {
1190 /* back out character overlay */
1191 field->buffers[0].string[pos] = old_c;
1192 }
1193
1194 _formi_wrap_field(field, ((pos > 0)? pos - 1 : 0));
1195 } else {
1196 field->buf0_status = TRUE;
1197 if ((field->rows + field->nrows) == 1) {
1198 if ((field->cursor_xpos < (field->cols - 1)) ||
1199 ((field->opts & O_STATIC) != O_STATIC))
1200 field->cursor_xpos++;
1201
1202 if (field->cursor_xpos > field->cols) {
1203 field->start_char++;
1204 field->cursor_xpos = field->cols;
1205 }
1206 } else {
1207 new_size = find_cur_line(field, pos);
1208 if (new_size >= field->rows) {
1209 field->cursor_ypos = field->rows - 1;
1210 field->start_line = field->row_count
1211 - field->cursor_ypos - 1;
1212 } else
1213 field->cursor_ypos = new_size;
1214
1215 if ((field->lines[new_size].start) <= (pos + 1)) {
1216 field->cursor_xpos = pos
1217 - field->lines[new_size].start + 1;
1218 } else {
1219 field->cursor_xpos = 0;
1220 }
1221
1222 /*
1223 * Annoying corner case - if we are right in
1224 * the bottom right corner of the field we
1225 * need to scroll the field one line so the
1226 * cursor is positioned correctly in the
1227 * field.
1228 */
1229 if ((field->cursor_xpos >= field->cols) &&
1230 (field->cursor_ypos == (field->rows - 1))) {
1231 field->cursor_ypos--;
1232 field->start_line++;
1233 }
1234 }
1235 }
1236
1237 #ifdef DEBUG
1238 assert((field->cursor_xpos <= field->cols)
1239 && (field->cursor_ypos < 400000)
1240 && (field->start_line < 400000));
1241
1242 fprintf(dbg,
1243 "add_char exit: xpos=%d, start=%d, length=%d(%d), allocated=%d\n",
1244 field->cursor_xpos, field->start_char,
1245 field->buffers[0].length, strlen(field->buffers[0].string),
1246 field->buffers[0].allocated);
1247 fprintf(dbg, "add_char exit: ypos=%d, start_line=%d\n",
1248 field->cursor_ypos, field->start_line);
1249 fprintf(dbg,"add_char exit: %s\n", field->buffers[0].string);
1250 fprintf(dbg, "add_char exit: buf0_status=%d\n", field->buf0_status);
1251 fprintf(dbg, "add_char exit: status = %s\n",
1252 (status == E_OK)? "OK" : "FAILED");
1253 #endif
1254 return status;
1255 }
1256
1257 /*
1258 * Manipulate the text in a field, this takes the given form and performs
1259 * the passed driver command on the current text field. Returns 1 if the
1260 * text field was modified.
1261 */
1262 int
1263 _formi_manipulate_field(FORM *form, int c)
1264 {
1265 FIELD *cur;
1266 char *str, saved;
1267 unsigned int i, start, end, pos, row, status, old_count;
1268 int len;
1269
1270 cur = form->fields[form->cur_field];
1271
1272 #ifdef DEBUG
1273 fprintf(dbg,
1274 "entry: xpos=%d, start_char=%d, length=%d, allocated=%d\n",
1275 cur->cursor_xpos, cur->start_char, cur->buffers[0].length,
1276 cur->buffers[0].allocated);
1277 fprintf(dbg, "entry: start_line=%d, ypos=%d\n", cur->start_line,
1278 cur->cursor_ypos);
1279 fprintf(dbg, "entry: string=");
1280 if (cur->buffers[0].string == NULL)
1281 fprintf(dbg, "(null)\n");
1282 else
1283 fprintf(dbg, "\"%s\"\n", cur->buffers[0].string);
1284 #endif
1285
1286 /* Cannot manipulate a null string! */
1287 if (cur->buffers[0].string == NULL)
1288 return E_REQUEST_DENIED;
1289
1290 switch (c) {
1291 case REQ_NEXT_CHAR:
1292 /* for a dynamic field allow an offset of one more
1293 * char so we can insert chars after end of string.
1294 * Static fields cannot do this so deny request if
1295 * cursor is at the end of the field.
1296 */
1297 if (((cur->opts & O_STATIC) == O_STATIC) &&
1298 (cur->cursor_xpos == cur->cols - 1) &&
1299 ((cur->rows + cur->nrows) == 1))
1300 return E_REQUEST_DENIED;
1301
1302 if ((cur->cursor_xpos + cur->start_char + 1)
1303 > cur->buffers[0].length)
1304 return E_REQUEST_DENIED;
1305
1306 if ((cur->rows + cur->nrows) == 1) {
1307 cur->cursor_xpos++;
1308 if (cur->cursor_xpos >= cur->cols - 1) {
1309 cur->cursor_xpos = cur->cols - 1;
1310 if ((cur->opts & O_STATIC) != O_STATIC)
1311 cur->start_char++;
1312 }
1313 } else {
1314 row = cur->start_line + cur->cursor_ypos;
1315 if (cur->cursor_xpos == (cur->lines[row].length - 1)) {
1316 if ((row + 1) >= cur->row_count)
1317 return E_REQUEST_DENIED;
1318
1319 cur->cursor_xpos = 0;
1320 if (cur->cursor_ypos == (cur->rows - 1))
1321 cur->start_line++;
1322 else
1323 cur->cursor_ypos++;
1324 } else
1325 cur->cursor_xpos++;
1326 }
1327
1328 break;
1329
1330 case REQ_PREV_CHAR:
1331 if ((cur->rows + cur->nrows) == 1) {
1332 if (cur->cursor_xpos == 0) {
1333 if (cur->start_char > 0)
1334 cur->start_char--;
1335 else
1336 return E_REQUEST_DENIED;
1337 } else
1338 cur->cursor_xpos--;
1339 } else {
1340 if ((cur->cursor_xpos == 0) &&
1341 (cur->cursor_ypos == 0) &&
1342 (cur->start_line == 0))
1343 return E_REQUEST_DENIED;
1344
1345 if (cur->cursor_xpos > 0) {
1346 cur->cursor_xpos--;
1347 } else {
1348 if (cur->cursor_ypos > 0)
1349 cur->cursor_ypos--;
1350 else
1351 cur->start_line--;
1352 cur->cursor_xpos =
1353 cur->lines[cur->start_line
1354 + cur->cursor_ypos].length
1355 - 1;
1356 }
1357 }
1358
1359 break;
1360
1361 case REQ_NEXT_LINE:
1362 if ((cur->start_line + cur->cursor_ypos + 1) >= cur->row_count)
1363 return E_REQUEST_DENIED;
1364
1365 if ((cur->cursor_ypos + 1) >= cur->rows) {
1366 cur->start_line++;
1367 } else
1368 cur->cursor_ypos++;
1369 break;
1370
1371 case REQ_PREV_LINE:
1372 if (cur->cursor_ypos == 0) {
1373 if (cur->start_line == 0)
1374 return E_REQUEST_DENIED;
1375 cur->start_line--;
1376 } else
1377 cur->cursor_ypos--;
1378 break;
1379
1380 case REQ_NEXT_WORD:
1381 start = cur->lines[cur->start_line + cur->cursor_ypos].start
1382 + cur->cursor_xpos + cur->start_char;
1383 str = cur->buffers[0].string;
1384
1385 start = find_eow(str, start);
1386
1387 /* check if we hit the end */
1388 if (str[start] == '\0')
1389 return E_REQUEST_DENIED;
1390
1391 /* otherwise we must have found the start of a word...*/
1392 if ((cur->rows + cur->nrows) == 1) {
1393 /* single line field */
1394 if (start - cur->start_char < cur->cols) {
1395 cur->cursor_xpos = start;
1396 } else {
1397 cur->start_char = start;
1398 cur->cursor_xpos = 0;
1399 }
1400 } else {
1401 /* multiline field */
1402 row = find_cur_line(cur, start);
1403 cur->cursor_xpos = start - cur->lines[row].start;
1404 if (row != (cur->start_line + cur->cursor_ypos)) {
1405 if (cur->cursor_ypos == (cur->rows - 1)) {
1406 cur->start_line = row - cur->rows + 1;
1407 } else {
1408 cur->cursor_ypos = row
1409 - cur->start_line;
1410 }
1411 }
1412 }
1413 break;
1414
1415 case REQ_PREV_WORD:
1416 start = cur->start_char + cur->cursor_xpos
1417 + cur->lines[cur->start_line + cur->cursor_ypos].start;
1418 if (cur->start_char > 0)
1419 start--;
1420
1421 if (start == 0)
1422 return E_REQUEST_DENIED;
1423
1424 str = cur->buffers[0].string;
1425
1426 start = find_sow(str, start);
1427
1428 if ((cur->rows + cur->nrows) == 1) {
1429 /* single line field */
1430 if (start - cur->start_char > 0) {
1431 cur->cursor_xpos = start;
1432 } else {
1433 cur->start_char = start;
1434 cur->cursor_xpos = 0;
1435 }
1436 } else {
1437 /* multiline field */
1438 row = find_cur_line(cur, start);
1439 cur->cursor_xpos = start - cur->lines[row].start;
1440 if (row != (cur->start_line + cur->cursor_ypos)) {
1441 if (cur->cursor_ypos == 0) {
1442 cur->start_line = row;
1443 } else {
1444 if (cur->start_line > row) {
1445 cur->start_line = row;
1446 cur->cursor_ypos = 0;
1447 } else {
1448 cur->cursor_ypos = row -
1449 cur->start_line;
1450 }
1451 }
1452 }
1453 }
1454
1455 break;
1456
1457 case REQ_BEG_FIELD:
1458 cur->start_char = 0;
1459 cur->start_line = 0;
1460 cur->cursor_xpos = 0;
1461 cur->cursor_ypos = 0;
1462 break;
1463
1464 case REQ_BEG_LINE:
1465 cur->cursor_xpos = 0;
1466 cur->start_char = 0;
1467 break;
1468
1469 case REQ_END_FIELD:
1470 if (cur->row_count > cur->rows) {
1471 cur->start_line = cur->row_count - cur->rows;
1472 cur->cursor_ypos = cur->rows - 1;
1473 } else {
1474 cur->start_line = 0;
1475 cur->cursor_ypos = cur->row_count - 1;
1476 }
1477
1478 /* we fall through here deliberately, we are on the
1479 * correct row, now we need to get to the end of the
1480 * line.
1481 */
1482 /* FALLTHRU */
1483
1484 case REQ_END_LINE:
1485 start = cur->lines[cur->start_line + cur->cursor_ypos].start;
1486 end = cur->lines[cur->start_line + cur->cursor_ypos].end;
1487
1488 if ((cur->rows + cur->nrows) == 1) {
1489 if (end - start > cur->cols - 1) {
1490 cur->cursor_xpos = cur->cols - 1;
1491 cur->start_char = end - cur->cols;
1492 if ((cur->opts & O_STATIC) != O_STATIC)
1493 cur->start_char++;
1494 } else {
1495 cur->cursor_xpos = end - start + 1;
1496 if (((cur->opts & O_STATIC) == O_STATIC) &&
1497 ((end - start) == (cur->cols - 1)))
1498 cur->cursor_xpos--;
1499
1500 cur->start_char = start;
1501 }
1502 } else {
1503 cur->cursor_xpos = end - start + 1;
1504 }
1505 break;
1506
1507 case REQ_LEFT_CHAR:
1508 if ((cur->cursor_xpos == 0) && (cur->start_char == 0)
1509 && (cur->start_line == 0) && (cur->cursor_ypos == 0))
1510 return E_REQUEST_DENIED;
1511
1512 if (cur->cursor_xpos == 0) {
1513 if ((cur->rows + cur->nrows) == 1) {
1514 if (cur->start_char > 0)
1515 cur->start_char--;
1516 else
1517 return E_REQUEST_DENIED;
1518 } else {
1519 if ((cur->cursor_ypos == 0) &&
1520 (cur->start_line == 0))
1521 return E_REQUEST_DENIED;
1522
1523 if (cur->cursor_ypos == 0)
1524 cur->start_line--;
1525 else
1526 cur->cursor_ypos--;
1527
1528 cur->cursor_xpos =
1529 cur->lines[cur->cursor_ypos
1530 + cur->start_line].length;
1531 }
1532 } else
1533 cur->cursor_xpos--;
1534 break;
1535
1536 case REQ_RIGHT_CHAR:
1537 pos = cur->start_char + cur->cursor_xpos;
1538 row = cur->start_line + cur->cursor_ypos;
1539 end = cur->lines[row].end;
1540
1541 if (cur->buffers[0].string[pos] == '\0')
1542 return E_REQUEST_DENIED;
1543
1544 #ifdef DEBUG
1545 fprintf(dbg, "req_right_char enter: start=%d, xpos=%d, c=%c\n",
1546 cur->start_char, cur->cursor_xpos,
1547 cur->buffers[0].string[pos]);
1548 #endif
1549
1550 if (pos == end) {
1551 start = pos + 1;
1552 if ((cur->buffers[0].length <= start)
1553 || ((row + 1) >= cur->row_count))
1554 return E_REQUEST_DENIED;
1555
1556 if ((cur->cursor_ypos + 1) >= cur->rows) {
1557 cur->start_line++;
1558 cur->cursor_ypos = cur->rows - 1;
1559 } else
1560 cur->cursor_ypos++;
1561
1562 cur->cursor_xpos = 0;
1563 } else {
1564 if (((cur->rows + cur->nrows) == 1) &&
1565 (cur->cursor_xpos == cur->cols - 1))
1566 cur->start_char++;
1567 else
1568 cur->cursor_xpos++;
1569 }
1570 #ifdef DEBUG
1571 fprintf(dbg, "req_right_char exit: start=%d, xpos=%d, c=%c\n",
1572 cur->start_char, cur->cursor_xpos,
1573 cur->buffers[0].string[cur->start_char +
1574 cur->cursor_xpos]);
1575 #endif
1576 break;
1577
1578 case REQ_UP_CHAR:
1579 if (cur->cursor_ypos == 0) {
1580 if (cur->start_line == 0)
1581 return E_REQUEST_DENIED;
1582
1583 cur->start_line--;
1584 } else
1585 cur->cursor_ypos--;
1586
1587 row = cur->start_line + cur->cursor_ypos;
1588
1589 if (cur->cursor_xpos > cur->lines[row].length)
1590 cur->cursor_xpos = cur->lines[row].length;
1591 break;
1592
1593 case REQ_DOWN_CHAR:
1594 if (cur->cursor_ypos == cur->rows - 1) {
1595 if (cur->start_line + cur->rows == cur->row_count)
1596 return E_REQUEST_DENIED;
1597 cur->start_line++;
1598 } else
1599 cur->cursor_ypos++;
1600
1601 row = cur->start_line + cur->cursor_ypos;
1602 if (cur->cursor_xpos > cur->lines[row].length)
1603 cur->cursor_xpos = cur->lines[row].length;
1604 break;
1605
1606 case REQ_NEW_LINE:
1607 if ((status = split_line(cur,
1608 cur->start_char + cur->cursor_xpos)) != E_OK)
1609 return status;
1610 break;
1611
1612 case REQ_INS_CHAR:
1613 if ((status = _formi_add_char(cur, cur->start_char
1614 + cur->cursor_xpos,
1615 cur->pad)) != E_OK)
1616 return status;
1617 break;
1618
1619 case REQ_INS_LINE:
1620 start = cur->lines[cur->start_line + cur->cursor_ypos].start;
1621 if ((status = split_line(cur, start)) != E_OK)
1622 return status;
1623 break;
1624
1625 case REQ_DEL_CHAR:
1626 if (cur->buffers[0].length == 0)
1627 return E_REQUEST_DENIED;
1628
1629 row = cur->start_line + cur->cursor_ypos;
1630 start = cur->start_char + cur->cursor_xpos
1631 + cur->lines[row].start;
1632 end = cur->buffers[0].length;
1633 if (start == cur->lines[row].end) {
1634 if ((cur->rows + cur->nrows) > 1) {
1635 if (cur->row_count > 1) {
1636 if (_formi_join_line(cur,
1637 start,
1638 JOIN_NEXT_NW)
1639 != E_OK) {
1640 return E_REQUEST_DENIED;
1641 }
1642 } else
1643 return E_REQUEST_DENIED;
1644 } else
1645 return E_REQUEST_DENIED;
1646 }
1647
1648 saved = cur->buffers[0].string[start];
1649 bcopy(&cur->buffers[0].string[start + 1],
1650 &cur->buffers[0].string[start],
1651 (unsigned) end - start + 1);
1652 cur->buffers[0].length--;
1653 bump_lines(cur, _FORMI_USE_CURRENT, -1, TRUE);
1654 if ((cur->rows + cur->nrows) > 1) {
1655 if (_formi_wrap_field(cur, start) != E_OK) {
1656 bcopy(&cur->buffers[0].string[start],
1657 &cur->buffers[0].string[start + 1],
1658 (unsigned) end - start);
1659 cur->buffers[0].length++;
1660 cur->buffers[0].string[start] = saved;
1661 bump_lines(cur, _FORMI_USE_CURRENT, 1, TRUE);
1662 _formi_wrap_field(cur, start);
1663 return E_REQUEST_DENIED;
1664 }
1665 }
1666 break;
1667
1668 case REQ_DEL_PREV:
1669 if ((cur->cursor_xpos == 0) && (cur->start_char == 0)
1670 && (cur->start_line == 0) && (cur->cursor_ypos == 0))
1671 return E_REQUEST_DENIED;
1672
1673 row = cur->start_line + cur->cursor_ypos;
1674 start = cur->cursor_xpos + cur->start_char
1675 + cur->lines[row].start;
1676 end = cur->buffers[0].length;
1677
1678 if ((cur->start_char + cur->cursor_xpos) == 0) {
1679 if (_formi_join_line(cur, cur->lines[row].start,
1680 JOIN_PREV_NW) != E_OK) {
1681 return E_REQUEST_DENIED;
1682 }
1683 }
1684
1685 saved = cur->buffers[0].string[start - 1];
1686 bcopy(&cur->buffers[0].string[start],
1687 &cur->buffers[0].string[start - 1],
1688 (unsigned) end - start + 1);
1689 bump_lines(cur, (int) start - 1, -1, TRUE);
1690 cur->buffers[0].length--;
1691
1692 if ((cur->rows + cur->nrows) == 1) {
1693 if ((cur->cursor_xpos == 0) && (cur->start_char > 0))
1694 cur->start_char--;
1695 else if ((cur->cursor_xpos == cur->cols - 1)
1696 && (cur->start_char > 0))
1697 cur->start_char--;
1698 else if (cur->cursor_xpos > 0)
1699 cur->cursor_xpos--;
1700 } else {
1701 pos = start - 1;
1702 if (pos >= cur->buffers[0].length)
1703 pos = cur->buffers[0].length - 1;
1704
1705 if ((_formi_wrap_field(cur, pos) != E_OK)) {
1706 bcopy(&cur->buffers[0].string[start - 1],
1707 &cur->buffers[0].string[start],
1708 (unsigned) end - start);
1709 cur->buffers[0].length++;
1710 cur->buffers[0].string[start - 1] = saved;
1711 bump_lines(cur, (int) start - 1, 1, TRUE);
1712 _formi_wrap_field(cur, pos);
1713 return E_REQUEST_DENIED;
1714 }
1715
1716 row = find_cur_line(cur, pos);
1717 cur->cursor_xpos = start - cur->lines[row].start - 1;
1718
1719 if (row >= cur->rows)
1720 cur->start_line = row - cur->cursor_ypos;
1721 else {
1722 cur->start_line = 0;
1723 cur->cursor_ypos = row;
1724 }
1725 }
1726 break;
1727
1728 case REQ_DEL_LINE:
1729 row = cur->start_line + cur->cursor_ypos;
1730 start = cur->lines[row].start;
1731 end = cur->lines[row].end;
1732 bcopy(&cur->buffers[0].string[end + 1],
1733 &cur->buffers[0].string[start],
1734 (unsigned) cur->buffers[0].length - end + 1);
1735
1736 if (((cur->rows + cur->nrows) == 1) ||
1737 (cur->row_count == 1)) {
1738 /* single line case */
1739 cur->buffers[0].length = 0;
1740 cur->lines[0].end = cur->lines[0].length = 0;
1741 cur->cursor_xpos = cur->cursor_ypos = 0;
1742 } else {
1743 /* multiline field */
1744 old_count = cur->row_count;
1745 cur->row_count--;
1746 if (cur->row_count == 0)
1747 cur->row_count = 1;
1748
1749 if (cur->row_count > 1)
1750 bcopy(&cur->lines[row + 1],
1751 &cur->lines[row],
1752 (unsigned) (cur->row_count - row)
1753 * sizeof(struct _formi_field_lines));
1754
1755 cur->lines[row].start = start;
1756 len = start - end - 1; /* yes, this is negative */
1757
1758 if (row < (cur->row_count - 1))
1759 bump_lines(cur, (int) start, len, FALSE);
1760 else if (old_count == 1) {
1761 cur->lines[0].end = cur->lines[0].length = 0;
1762 cur->cursor_xpos = 0;
1763 cur->cursor_ypos = 0;
1764 } else if (cur->row_count == 1) {
1765 cur->lines[0].length = cur->buffers[0].length
1766 + len;
1767 cur->lines[0].end = cur->lines[0].length - 1;
1768 }
1769
1770 cur->buffers[0].length += len;
1771
1772 if (row > (cur->row_count - 1)) {
1773 row--;
1774 if (cur->cursor_ypos == 0) {
1775 if (cur->start_line > 0) {
1776 cur->start_line--;
1777 }
1778 } else {
1779 cur->cursor_ypos--;
1780 }
1781 }
1782
1783 if (old_count > 1) {
1784 if (cur->cursor_xpos > cur->lines[row].length)
1785 cur->cursor_xpos =
1786 cur->lines[row].length - 1;
1787 if (row >= cur->rows)
1788 cur->start_line = row
1789 - cur->cursor_ypos;
1790 else {
1791 cur->start_line = 0;
1792 cur->cursor_ypos = row;
1793 }
1794 }
1795 }
1796 break;
1797
1798 case REQ_DEL_WORD:
1799 start = cur->start_char + cur->cursor_xpos;
1800 end = find_eow(cur->buffers[0].string, start);
1801 start = find_sow(cur->buffers[0].string, start);
1802 bcopy(&cur->buffers[0].string[end + 1],
1803 &cur->buffers[0].string[start],
1804 (unsigned) cur->buffers[0].length - end + 1);
1805 len = end - start;
1806 cur->buffers[0].length -= len;
1807 bump_lines(cur, _FORMI_USE_CURRENT, - (int) len, TRUE);
1808
1809 if (cur->cursor_xpos > cur->lines[row].length)
1810 cur->cursor_xpos = cur->lines[row].length;
1811 break;
1812
1813 case REQ_CLR_EOL:
1814 row = cur->start_line + cur->cursor_ypos;
1815 start = cur->start_char + cur->cursor_xpos;
1816 end = cur->lines[row].end;
1817 len = end - start;
1818 bcopy(&cur->buffers[0].string[end + 1],
1819 &cur->buffers[0].string[start],
1820 cur->buffers[0].length - end + 1);
1821 cur->buffers[0].length -= len;
1822 bump_lines(cur, _FORMI_USE_CURRENT, - (int) len, TRUE);
1823
1824 if (cur->cursor_xpos > cur->lines[row].length)
1825 cur->cursor_xpos = cur->lines[row].length;
1826 break;
1827
1828 case REQ_CLR_EOF:
1829 row = cur->start_line + cur->cursor_ypos;
1830 cur->buffers[0].string[cur->start_char
1831 + cur->cursor_xpos] = '\0';
1832 cur->buffers[0].length = strlen(cur->buffers[0].string);
1833 cur->lines[row].end = cur->buffers[0].length;
1834 cur->lines[row].length = cur->lines[row].end
1835 - cur->lines[row].start;
1836
1837 for (i = cur->start_char + cur->cursor_xpos;
1838 i < cur->buffers[0].length; i++)
1839 cur->buffers[0].string[i] = cur->pad;
1840 break;
1841
1842 case REQ_CLR_FIELD:
1843 cur->buffers[0].string[0] = '\0';
1844 cur->buffers[0].length = 0;
1845 cur->row_count = 1;
1846 cur->start_line = 0;
1847 cur->cursor_ypos = 0;
1848 cur->cursor_xpos = 0;
1849 cur->start_char = 0;
1850 cur->lines[0].start = 0;
1851 cur->lines[0].end = 0;
1852 cur->lines[0].length = 0;
1853 break;
1854
1855 case REQ_OVL_MODE:
1856 cur->overlay = 1;
1857 break;
1858
1859 case REQ_INS_MODE:
1860 cur->overlay = 0;
1861 break;
1862
1863 case REQ_SCR_FLINE:
1864 _formi_scroll_fwd(cur, 1);
1865 break;
1866
1867 case REQ_SCR_BLINE:
1868 _formi_scroll_back(cur, 1);
1869 break;
1870
1871 case REQ_SCR_FPAGE:
1872 _formi_scroll_fwd(cur, cur->rows);
1873 break;
1874
1875 case REQ_SCR_BPAGE:
1876 _formi_scroll_back(cur, cur->rows);
1877 break;
1878
1879 case REQ_SCR_FHPAGE:
1880 _formi_scroll_fwd(cur, cur->rows / 2);
1881 break;
1882
1883 case REQ_SCR_BHPAGE:
1884 _formi_scroll_back(cur, cur->rows / 2);
1885 break;
1886
1887 case REQ_SCR_FCHAR:
1888 _formi_hscroll_fwd(cur, 1);
1889 break;
1890
1891 case REQ_SCR_BCHAR:
1892 _formi_hscroll_back(cur, 1);
1893 break;
1894
1895 case REQ_SCR_HFLINE:
1896 _formi_hscroll_fwd(cur, cur->cols);
1897 break;
1898
1899 case REQ_SCR_HBLINE:
1900 _formi_hscroll_back(cur, cur->cols);
1901 break;
1902
1903 case REQ_SCR_HFHALF:
1904 _formi_hscroll_fwd(cur, cur->cols / 2);
1905 break;
1906
1907 case REQ_SCR_HBHALF:
1908 _formi_hscroll_back(cur, cur->cols / 2);
1909 break;
1910
1911 default:
1912 return 0;
1913 }
1914
1915 #ifdef DEBUG
1916 fprintf(dbg, "exit: xpos=%d, start_char=%d, length=%d, allocated=%d\n",
1917 cur->cursor_xpos, cur->start_char, cur->buffers[0].length,
1918 cur->buffers[0].allocated);
1919 fprintf(dbg, "exit: start_line=%d, ypos=%d\n", cur->start_line,
1920 cur->cursor_ypos);
1921 fprintf(dbg, "exit: string=\"%s\"\n", cur->buffers[0].string);
1922 #endif
1923 return 1;
1924 }
1925
1926 /*
1927 * Validate the give character by passing it to any type character
1928 * checking routines, if they exist.
1929 */
1930 int
1931 _formi_validate_char(FIELD *field, char c)
1932 {
1933 int ret_val;
1934
1935 if (field->type == NULL)
1936 return E_OK;
1937
1938 ret_val = E_INVALID_FIELD;
1939 _formi_do_char_validation(field, field->type, c, &ret_val);
1940
1941 return ret_val;
1942 }
1943
1944
1945 /*
1946 * Perform the validation of the character, invoke all field_type validation
1947 * routines. If the field is ok then update ret_val to E_OK otherwise
1948 * ret_val is not changed.
1949 */
1950 static void
1951 _formi_do_char_validation(FIELD *field, FIELDTYPE *type, char c, int *ret_val)
1952 {
1953 if ((type->flags & _TYPE_IS_LINKED) == _TYPE_IS_LINKED) {
1954 _formi_do_char_validation(field, type->link->next, c, ret_val);
1955 _formi_do_char_validation(field, type->link->prev, c, ret_val);
1956 } else {
1957 if (type->char_check == NULL)
1958 *ret_val = E_OK;
1959 else {
1960 if (type->char_check((int)(unsigned char) c,
1961 field->args) == TRUE)
1962 *ret_val = E_OK;
1963 }
1964 }
1965 }
1966
1967 /*
1968 * Validate the current field. If the field validation returns success then
1969 * return E_OK otherwise return E_INVALID_FIELD.
1970 *
1971 */
1972 int
1973 _formi_validate_field(FORM *form)
1974 {
1975 FIELD *cur;
1976 char *bp;
1977 int ret_val, count;
1978
1979
1980 if ((form == NULL) || (form->fields == NULL) ||
1981 (form->fields[0] == NULL))
1982 return E_INVALID_FIELD;
1983
1984 cur = form->fields[form->cur_field];
1985
1986 bp = cur->buffers[0].string;
1987 count = _formi_skip_blanks(bp, 0);
1988
1989 /* check if we have a null field, depending on the nullok flag
1990 * this may be acceptable or not....
1991 */
1992 if (cur->buffers[0].string[count] == '\0') {
1993 if ((cur->opts & O_NULLOK) == O_NULLOK)
1994 return E_OK;
1995 else
1996 return E_INVALID_FIELD;
1997 }
1998
1999 /* check if an unmodified field is ok */
2000 if (cur->buf0_status == 0) {
2001 if ((cur->opts & O_PASSOK) == O_PASSOK)
2002 return E_OK;
2003 else
2004 return E_INVALID_FIELD;
2005 }
2006
2007 /* if there is no type then just accept the field */
2008 if (cur->type == NULL)
2009 return E_OK;
2010
2011 ret_val = E_INVALID_FIELD;
2012 _formi_do_validation(cur, cur->type, &ret_val);
2013
2014 return ret_val;
2015 }
2016
2017 /*
2018 * Perform the validation of the field, invoke all field_type validation
2019 * routines. If the field is ok then update ret_val to E_OK otherwise
2020 * ret_val is not changed.
2021 */
2022 static void
2023 _formi_do_validation(FIELD *field, FIELDTYPE *type, int *ret_val)
2024 {
2025 if ((type->flags & _TYPE_IS_LINKED) == _TYPE_IS_LINKED) {
2026 _formi_do_validation(field, type->link->next, ret_val);
2027 _formi_do_validation(field, type->link->prev, ret_val);
2028 } else {
2029 if (type->field_check == NULL)
2030 *ret_val = E_OK;
2031 else {
2032 if (type->field_check(field, field_buffer(field, 0))
2033 == TRUE)
2034 *ret_val = E_OK;
2035 }
2036 }
2037 }
2038
2039 /*
2040 * Select the next/previous choice for the field, the driver command
2041 * selecting the direction will be passed in c. Return 1 if a choice
2042 * selection succeeded, 0 otherwise.
2043 */
2044 int
2045 _formi_field_choice(FORM *form, int c)
2046 {
2047 FIELDTYPE *type;
2048 FIELD *field;
2049
2050 if ((form == NULL) || (form->fields == NULL) ||
2051 (form->fields[0] == NULL) ||
2052 (form->fields[form->cur_field]->type == NULL))
2053 return 0;
2054
2055 field = form->fields[form->cur_field];
2056 type = field->type;
2057
2058 switch (c) {
2059 case REQ_NEXT_CHOICE:
2060 if (type->next_choice == NULL)
2061 return 0;
2062 else
2063 return type->next_choice(field,
2064 field_buffer(field, 0));
2065
2066 case REQ_PREV_CHOICE:
2067 if (type->prev_choice == NULL)
2068 return 0;
2069 else
2070 return type->prev_choice(field,
2071 field_buffer(field, 0));
2072
2073 default: /* should never happen! */
2074 return 0;
2075 }
2076 }
2077
2078 /*
2079 * Update the fields if they have changed. The parameter old has the
2080 * previous current field as the current field may have been updated by
2081 * the driver. Return 1 if the form page needs updating.
2082 *
2083 */
2084 int
2085 _formi_update_field(FORM *form, int old_field)
2086 {
2087 int cur, i;
2088
2089 cur = form->cur_field;
2090
2091 if (old_field != cur) {
2092 if (!((cur >= form->page_starts[form->page].first) &&
2093 (cur <= form->page_starts[form->page].last))) {
2094 /* not on same page any more */
2095 for (i = 0; i < form->max_page; i++) {
2096 if ((form->page_starts[i].in_use == 1) &&
2097 (form->page_starts[i].first <= cur) &&
2098 (form->page_starts[i].last >= cur)) {
2099 form->page = i;
2100 return 1;
2101 }
2102 }
2103 }
2104 }
2105
2106 _formi_redraw_field(form, old_field);
2107 _formi_redraw_field(form, form->cur_field);
2108 return 0;
2109 }
2110
2111 /*
2112 * Compare function for the field sorting
2113 *
2114 */
2115 static int
2116 field_sort_compare(const void *one, const void *two)
2117 {
2118 const FIELD *a, *b;
2119 int tl;
2120
2121 /* LINTED const castaway; we don't modify these! */
2122 a = (const FIELD *) *((const FIELD **) one);
2123 b = (const FIELD *) *((const FIELD **) two);
2124
2125 if (a == NULL)
2126 return 1;
2127
2128 if (b == NULL)
2129 return -1;
2130
2131 /*
2132 * First check the page, we want the fields sorted by page.
2133 *
2134 */
2135 if (a->page != b->page)
2136 return ((a->page > b->page)? 1 : -1);
2137
2138 tl = _formi_top_left(a->parent, a->index, b->index);
2139
2140 /*
2141 * sort fields left to right, top to bottom so the top left is
2142 * the less than value....
2143 */
2144 return ((tl == a->index)? -1 : 1);
2145 }
2146
2147 /*
2148 * Sort the fields in a form ready for driver traversal.
2149 */
2150 void
2151 _formi_sort_fields(FORM *form)
2152 {
2153 FIELD **sort_area;
2154 int i;
2155
2156 CIRCLEQ_INIT(&form->sorted_fields);
2157
2158 if ((sort_area = (FIELD **) malloc(sizeof(FIELD *) * form->field_count))
2159 == NULL)
2160 return;
2161
2162 bcopy(form->fields, sort_area, sizeof(FIELD *) * form->field_count);
2163 qsort(sort_area, (unsigned) form->field_count, sizeof(FIELD *),
2164 field_sort_compare);
2165
2166 for (i = 0; i < form->field_count; i++)
2167 CIRCLEQ_INSERT_TAIL(&form->sorted_fields, sort_area[i], glue);
2168
2169 free(sort_area);
2170 }
2171
2172 /*
2173 * Set the neighbours for all the fields in the given form.
2174 */
2175 void
2176 _formi_stitch_fields(FORM *form)
2177 {
2178 int above_row, below_row, end_above, end_below, cur_row, real_end;
2179 FIELD *cur, *above, *below;
2180
2181 /*
2182 * check if the sorted fields circle queue is empty, just
2183 * return if it is.
2184 */
2185 if (CIRCLEQ_EMPTY(&form->sorted_fields))
2186 return;
2187
2188 /* initially nothing is above..... */
2189 above_row = -1;
2190 end_above = TRUE;
2191 above = NULL;
2192
2193 /* set up the first field as the current... */
2194 cur = CIRCLEQ_FIRST(&form->sorted_fields);
2195 cur_row = cur->form_row;
2196
2197 /* find the first field on the next row if any */
2198 below = CIRCLEQ_NEXT(cur, glue);
2199 below_row = -1;
2200 end_below = TRUE;
2201 real_end = TRUE;
2202 while (below != (void *)&form->sorted_fields) {
2203 if (below->form_row != cur_row) {
2204 below_row = below->form_row;
2205 end_below = FALSE;
2206 real_end = FALSE;
2207 break;
2208 }
2209 below = CIRCLEQ_NEXT(below, glue);
2210 }
2211
2212 /* walk the sorted fields, setting the neighbour pointers */
2213 while (cur != (void *) &form->sorted_fields) {
2214 if (cur == CIRCLEQ_FIRST(&form->sorted_fields))
2215 cur->left = NULL;
2216 else
2217 cur->left = CIRCLEQ_PREV(cur, glue);
2218
2219 if (cur == CIRCLEQ_LAST(&form->sorted_fields))
2220 cur->right = NULL;
2221 else
2222 cur->right = CIRCLEQ_NEXT(cur, glue);
2223
2224 if (end_above == TRUE)
2225 cur->up = NULL;
2226 else {
2227 cur->up = above;
2228 above = CIRCLEQ_NEXT(above, glue);
2229 if (above_row != above->form_row) {
2230 end_above = TRUE;
2231 above_row = above->form_row;
2232 }
2233 }
2234
2235 if (end_below == TRUE)
2236 cur->down = NULL;
2237 else {
2238 cur->down = below;
2239 below = CIRCLEQ_NEXT(below, glue);
2240 if (below == (void *) &form->sorted_fields) {
2241 end_below = TRUE;
2242 real_end = TRUE;
2243 } else if (below_row != below->form_row) {
2244 end_below = TRUE;
2245 below_row = below->form_row;
2246 }
2247 }
2248
2249 cur = CIRCLEQ_NEXT(cur, glue);
2250 if ((cur != (void *) &form->sorted_fields)
2251 && (cur_row != cur->form_row)) {
2252 cur_row = cur->form_row;
2253 if (end_above == FALSE) {
2254 for (; above != CIRCLEQ_FIRST(&form->sorted_fields);
2255 above = CIRCLEQ_NEXT(above, glue)) {
2256 if (above->form_row != above_row) {
2257 above_row = above->form_row;
2258 break;
2259 }
2260 }
2261 } else if (above == NULL) {
2262 above = CIRCLEQ_FIRST(&form->sorted_fields);
2263 end_above = FALSE;
2264 above_row = above->form_row;
2265 } else
2266 end_above = FALSE;
2267
2268 if (end_below == FALSE) {
2269 while (below_row == below->form_row) {
2270 below = CIRCLEQ_NEXT(below,
2271 glue);
2272 if (below ==
2273 (void *)&form->sorted_fields) {
2274 real_end = TRUE;
2275 end_below = TRUE;
2276 break;
2277 }
2278 }
2279
2280 if (below != (void *)&form->sorted_fields)
2281 below_row = below->form_row;
2282 } else if (real_end == FALSE)
2283 end_below = FALSE;
2284
2285 }
2286 }
2287 }
2288