internals.c revision 1.1 1 /* $NetBSD: internals.c,v 1.1 2000/12/17 12:04:30 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 withough 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 <ctype.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <strings.h>
36 #include "internals.h"
37 #include "form.h"
38
39 /* define our own min function - this is not generic but will do here
40 * (don't believe me? think about what value you would get
41 * from min(x++, y++)
42 */
43 #define min(a,b) (((a) > (b))? (b) : (a))
44
45 /* for the line joining function... */
46 #define JOIN_NEXT 1
47 #define JOIN_NEXT_NW 2 /* next join, don't wrap the joined line */
48 #define JOIN_PREV 3
49 #define JOIN_PREV_NW 4 /* previous join, don't wrap the joined line */
50
51 static void
52 _formi_do_validation(FIELD *field, FIELDTYPE *type, int *ret_val);
53 static int
54 _formi_join_line(FIELD *field, char *str, unsigned int pos, int direction);
55 static int
56 _formi_wrap_field(FIELD *field, unsigned int pos);
57 static void
58 _formi_redraw_field(FORM *form, int field);
59 void
60 _formi_hscroll_back(FIELD *field, unsigned int amt);
61 void
62 _formi_hscroll_fwd(FIELD *field, unsigned int amt);
63 static void
64 _formi_scroll_back(FIELD *field, unsigned int amt);
65 static void
66 _formi_scroll_fwd(FIELD *field, unsigned int amt);
67 static int
68 find_sow(char *str, unsigned int offset);
69 static int
70 find_cur_line(FIELD *cur);
71
72 /*
73 * Set the form's current field to the first valid field on the page.
74 * Assume the fields have been sorted and stitched.
75 */
76 int
77 _formi_pos_first_field(FORM *form)
78 {
79 FIELD *cur;
80 int old_page;
81
82 old_page = form->page;
83
84 /* scan forward for an active page....*/
85 while (form->page_starts[form->page].in_use == 0) {
86 form->page++;
87 if (form->page > form->max_page) {
88 form->page = old_page;
89 return E_REQUEST_DENIED;
90 }
91 }
92
93 cur = form->fields[form->page_starts[form->page].first];
94 while ((cur->opts & (O_VISIBLE | O_ACTIVE))
95 != (O_VISIBLE | O_ACTIVE)) {
96 cur = CIRCLEQ_NEXT(cur, glue);
97 if (cur == (void *) &form->sorted_fields) {
98 form->page = old_page;
99 return E_REQUEST_DENIED;
100 }
101 }
102
103 form->cur_field = cur->index;
104 return E_OK;
105 }
106
107 /*
108 * Set the field to the next active and visible field, the fields are
109 * traversed in index order in the direction given. If the parameter
110 * use_sorted is TRUE then the sorted field list will be traversed instead
111 * of using the field index.
112 */
113 int
114 _formi_pos_new_field(FORM *form, unsigned direction, unsigned use_sorted)
115 {
116 FIELD *cur;
117 int i;
118
119 i = form->cur_field;
120 cur = form->fields[i];
121
122 do {
123 if (direction == _FORMI_FORWARD) {
124 if (use_sorted == TRUE) {
125 cur = CIRCLEQ_NEXT(cur, glue);
126 i = cur->index;
127 } else {
128 i++;
129 if (i >= form->field_count)
130 i = 0;
131 }
132 } else {
133 if (use_sorted == TRUE) {
134 cur = CIRCLEQ_PREV(cur, glue);
135 i = cur->index;
136 } else {
137 i--;
138 if (i < 0)
139 i = form->field_count - 1;
140 }
141 }
142
143 if ((form->fields[i]->opts & (O_VISIBLE | O_ACTIVE))
144 == (O_VISIBLE | O_ACTIVE)) {
145 form->cur_field = i;
146 return E_OK;
147 }
148 }
149 while (i != form->cur_field);
150
151 return E_REQUEST_DENIED;
152 }
153
154 /*
155 * Find the line in a field that the cursor is currently on.
156 */
157 static int
158 find_cur_line(FIELD *cur)
159 {
160 unsigned start, end, pos, row;
161 const char *str;
162
163 str = cur->buffers[0].string;
164 pos = cur->start_char + cur->hscroll + cur->cursor_xpos;
165
166 start = 0;
167 end = 0;
168
169 for (row = 0; row < cur->row_count; row++) {
170 start = _formi_find_bol(str, start);
171 end = _formi_find_eol(str, end);
172 if ((pos >= start) && (pos <= end))
173 return row;
174 }
175
176 return 0;
177 }
178
179
180 /*
181 * Word wrap the contents of the field's buffer 0 if this is allowed.
182 * If the wrap is successful, that is, the row count nor the buffer
183 * size is exceeded then the function will return E_OK, otherwise it
184 * will return E_REQUEST_DENIED.
185 */
186 static int
187 _formi_wrap_field(FIELD *field, unsigned int pos)
188 {
189 char *str, *new;
190 int width, length, allocated, row_count, sol, eol, wrapped;
191 size_t new_size;
192
193 if ((field->opts & O_WRAP) != O_WRAP)
194 return E_REQUEST_DENIED;
195
196 wrapped = FALSE;
197 row_count = 0;
198 allocated = field->buffers[0].allocated;
199 length = field->buffers[0].length;
200 if ((str = (char *) malloc(sizeof(char) * allocated)) == NULL)
201 return E_SYSTEM_ERROR;
202
203 strcpy(str,field->buffers[0].string);
204
205 if ((field->opts & O_STATIC) == O_STATIC)
206 width = field->cols;
207 else
208 width = field->dcols;
209
210 while (str[pos] != '\0') {
211 row_count++;
212 sol = _formi_find_bol(str, pos);
213 eol = _formi_find_eol(str, pos);
214 if ((eol - sol) <= width) {
215 /* line may be too short, try joining some lines */
216 pos = eol;
217 if ((eol - sol) == width) {
218 /* if line is just right then don't wrap */
219 pos++;
220 continue;
221 }
222
223 if (_formi_join_line(field, str, pos, JOIN_NEXT_NW)
224 == E_OK) {
225 row_count--; /* cuz we just joined a line */
226 wrapped = TRUE;
227 } else
228 break;
229 } else {
230 /* line is too long, split it - maybe */
231 /* split on first whitespace before current word */
232 pos = sol + width;
233 if (!isblank(str[pos]))
234 pos = find_sow(str, pos);
235
236 if (pos != sol) {
237 if (length + 1 >= allocated) {
238 new_size = allocated + 64
239 - (allocated % 64);
240
241 if ((new = (char *) realloc(str,
242 sizeof(char) * new_size)
243 ) == NULL) {
244 free(str);
245 return E_SYSTEM_ERROR;
246 }
247 str = new;
248 allocated = new_size;
249 }
250
251 bcopy(&str[pos], &str[pos + 1],
252 (unsigned) length - pos - 1);
253 str[pos] = '\n';
254 pos = pos + 1;
255 length++;
256 wrapped = TRUE;
257 } else
258 break;
259 }
260 }
261
262 if (row_count > field->rows) {
263 free(str);
264 return E_REQUEST_DENIED;
265 }
266
267 if (wrapped == TRUE) {
268 field->buffers[0].length = length;
269 field->buffers[0].allocated = allocated;
270 free(field->buffers[0].string);
271 field->buffers[0].string = str;
272 } else /* all that work was in vain.... */
273 free(str);
274
275 return E_OK;
276 }
277
278 /*
279 * Join the two lines that surround the location pos, the type
280 * variable indicates the direction of the join. Note that pos is
281 * assumed to be at either the end of the line for a JOIN_NEXT or at
282 * the beginning of the line for a JOIN_PREV. We need to check the
283 * field options to ensure the join does not overflow the line limit
284 * (if wrap is off) or wrap the field buffer again. Returns E_OK if
285 * the join was successful or E_REQUEST_DENIED if the join cannot
286 * happen.
287 */
288 static int
289 _formi_join_line(FIELD *field, char *str, unsigned int pos, int direction)
290 {
291 unsigned int len, sol, eol, npos, start, dest;
292
293 npos = pos;
294
295 if ((direction == JOIN_NEXT) || (direction == JOIN_NEXT_NW)) {
296 sol = _formi_find_bol(str, pos);
297 npos++;
298 /* see if there is another line following... */
299 if (str[npos] == '\0')
300 return E_REQUEST_DENIED;
301 eol = _formi_find_eol(str, npos);
302
303 start = npos;
304 dest = pos;
305 len = eol - npos;
306 } else {
307 if (pos == 0)
308 return E_REQUEST_DENIED;
309 eol = _formi_find_eol(str, pos);
310 npos--;
311 sol = _formi_find_bol(str, npos);
312
313 start = pos;
314 dest = npos;
315 len = eol - pos;
316 }
317
318
319 /* if we cannot wrap and the length of the resultant line
320 * is bigger than our field width we shall deny the request.
321 */
322 if (((field->opts & O_WRAP) != O_WRAP) && /* XXXXX check for dynamic field */
323 ((sol + eol - 1) > field->cols))
324 return E_REQUEST_DENIED;
325
326 bcopy(&str[start], &str[dest], (unsigned) len);
327
328 /* wrap the field if required, if this fails undo the change */
329 if ((direction == JOIN_NEXT) || (direction == JOIN_PREV)) {
330 if (_formi_wrap_field(field, (unsigned int) pos) != E_OK) {
331 bcopy(&str[dest], &str[start], (unsigned) len);
332 str[dest] = '\n';
333 return E_REQUEST_DENIED;
334 }
335 }
336
337 return E_OK;
338 }
339
340 /*
341 * skip the blanks in the given string, start at the index start and
342 * continue forward until either the end of the string or a non-blank
343 * character is found. Return the index of either the end of the string or
344 * the first non-blank character.
345 */
346 unsigned
347 skip_blanks(char *string, unsigned int start)
348 {
349 unsigned int i;
350
351 i = start;
352
353 while ((string[i] != '\0') && isblank(string[i]))
354 i++;
355
356 return i;
357 }
358
359 /*
360 * Return the index of the top left most field of the two given fields.
361 */
362 static int
363 _formi_top_left(FORM *form, int a, int b)
364 {
365 /* lower row numbers always win here.... */
366 if (form->fields[a]->form_row < form->fields[b]->form_row)
367 return a;
368
369 if (form->fields[a]->form_row > form->fields[b]->form_row)
370 return b;
371
372 /* rows must be equal, check columns */
373 if (form->fields[a]->form_col < form->fields[b]->form_col)
374 return a;
375
376 if (form->fields[a]->form_col > form->fields[b]->form_col)
377 return b;
378
379 /* if we get here fields must be in exactly the same place, punt */
380 return a;
381 }
382
383 /*
384 * Return the index to the field that is the bottom-right-most of the
385 * two given fields.
386 */
387 static int
388 _formi_bottom_right(FORM *form, int a, int b)
389 {
390 /* check the rows first, biggest row wins */
391 if (form->fields[a]->form_row > form->fields[b]->form_row)
392 return a;
393 if (form->fields[a]->form_row < form->fields[b]->form_row)
394 return b;
395
396 /* rows must be equal, check cols, biggest wins */
397 if (form->fields[a]->form_col > form->fields[b]->form_col)
398 return a;
399 if (form->fields[a]->form_col < form->fields[b]->form_col)
400 return b;
401
402 /* fields in the same place, punt */
403 return a;
404 }
405
406 /*
407 * Find the next '\n' character in the given string starting at offset
408 * if there are no newlines found then return the index to the end of the
409 * string.
410 */
411 int
412 _formi_find_eol(const char *string, unsigned int offset)
413 {
414 char *location;
415 int eol;
416
417 if ((location = index(&string[offset], '\n')) != NULL)
418 eol = location - string;
419 else
420 eol = strlen(string);
421
422 if (eol > 0)
423 eol--;
424
425 return eol;
426 }
427
428 /*
429 * Find the previous '\n' character in the given string starting at offset
430 * if there are no newlines found then return 0.
431 */
432 int
433 _formi_find_bol(const char *string, unsigned int offset)
434 {
435 int cnt;
436
437 cnt = offset;
438 while ((cnt > 0) && (string[cnt] != '\n'))
439 cnt--;
440
441 /* if we moved and found a newline go forward one to point at the
442 * actual start of the line....
443 */
444 if ((cnt != offset) && (string[cnt] == '\n'))
445 cnt++;
446
447 return cnt;
448 }
449
450 /*
451 * Find the end of the current word in the string str, starting at
452 * offset - the end includes any trailing whitespace. If the end of
453 * the string is found before a new word then just return the offset
454 * to the end of the string.
455 */
456 static int
457 find_eow(char *str, unsigned int offset)
458 {
459 int start;
460
461 start = offset;
462 /* first skip any non-whitespace */
463 while ((str[start] != '\0') && !isblank(str[start]))
464 start++;
465
466 /* see if we hit the end of the string */
467 if (str[start] == '\0')
468 return start;
469
470 /* otherwise skip the whitespace.... */
471 while ((str[start] != '\0') && isblank(str[start]))
472 start++;
473
474 return start;
475 }
476
477 /*
478 * Find the beginning of the current word in the string str, starting
479 * at offset.
480 */
481 static int
482 find_sow(char *str, unsigned int offset)
483 {
484 int start;
485
486 start = offset;
487
488 if (start > 0) {
489 if (isblank(str[start]) || isblank(str[start - 1])) {
490 if (isblank(str[start - 1]))
491 start--;
492 /* skip the whitespace.... */
493 while ((start >= 0) && isblank(str[start]))
494 start--;
495 }
496 }
497
498 /* see if we hit the start of the string */
499 if (start < 0)
500 return 0;
501
502 /* now skip any non-whitespace */
503 while ((start >= 0) && !isblank(str[start]))
504 start--;
505
506 if (start > 0)
507 start++; /* last loop has us pointing at a space, adjust */
508
509 if (start < 0)
510 start = 0;
511
512 return start;
513 }
514
515 /*
516 * Scroll the field forward the given number of lines.
517 */
518 static void
519 _formi_scroll_fwd(FIELD *field, unsigned int amt)
520 {
521 /* check if we have lines to scroll */
522 if (field->row_count < (field->start_line + field->rows))
523 return;
524
525 field->start_line += min(amt,
526 field->row_count - field->start_line
527 - field->rows);
528 }
529
530 /*
531 * Scroll the field backward the given number of lines.
532 */
533 static void
534 _formi_scroll_back(FIELD *field, unsigned int amt)
535 {
536 if (field->start_line == 0)
537 return;
538
539 field->start_line -= min(field->start_line, amt);
540 }
541
542 /*
543 * Scroll the field forward the given number of characters.
544 */
545 void
546 _formi_hscroll_fwd(FIELD *field, int unsigned amt)
547 {
548 int end, scroll_amt;
549
550 end = _formi_find_eol(field->buffers[0].string,
551 field->start_char + field->hscroll
552 + field->cursor_xpos) - field->start_char
553 - field->hscroll - field->cursor_xpos;
554
555 scroll_amt = min(amt, end);
556 if (scroll_amt < 0)
557 scroll_amt = 0;
558
559 field->hscroll += scroll_amt;
560 if (amt > field->cursor_xpos)
561 field->cursor_xpos = 0;
562 else
563 field->cursor_xpos -= scroll_amt;
564 }
565
566 /*
567 * Scroll the field backward the given number of characters.
568 */
569 void
570 _formi_hscroll_back(FIELD *field, unsigned int amt)
571 {
572 int flen, sa;
573
574 sa = min(field->hscroll, amt);
575 field->hscroll -= sa;
576 field->cursor_xpos += sa;
577 flen = field->cols;
578 if (field->start_char > 0)
579 flen--;
580 if (field->cursor_xpos > flen)
581 field->cursor_xpos = flen;
582 }
583
584 /*
585 * Find the different pages in the form fields and assign the form
586 * page_starts array with the information to find them.
587 */
588 int
589 _formi_find_pages(FORM *form)
590 {
591 int i, cur_page = 0;
592
593 if ((form->page_starts = (_FORMI_PAGE_START *)
594 malloc((form->max_page + 1) * sizeof(_FORMI_PAGE_START))) == NULL)
595 return E_SYSTEM_ERROR;
596
597 /* initialise the page starts array */
598 memset(form->page_starts, 0,
599 (form->max_page + 1) * sizeof(_FORMI_PAGE_START));
600
601 for (i =0; i < form->field_count; i++) {
602 if (form->fields[i]->page_break == 1)
603 cur_page++;
604 if (form->page_starts[cur_page].in_use == 0) {
605 form->page_starts[cur_page].in_use = 1;
606 form->page_starts[cur_page].first = i;
607 form->page_starts[cur_page].last = i;
608 form->page_starts[cur_page].top_left = i;
609 form->page_starts[cur_page].bottom_right = i;
610 } else {
611 form->page_starts[cur_page].last = i;
612 form->page_starts[cur_page].top_left =
613 _formi_top_left(form,
614 form->page_starts[cur_page].top_left,
615 i);
616 form->page_starts[cur_page].bottom_right =
617 _formi_bottom_right(form,
618 form->page_starts[cur_page].bottom_right,
619 i);
620 }
621 }
622
623 return E_OK;
624 }
625
626 /*
627 * Completely redraw the field of the given form.
628 */
629 static void
630 _formi_redraw_field(FORM *form, int field)
631 {
632 unsigned int pre, post, flen, slen, i, row, start, end, offset;
633 char *str;
634 FIELD *cur;
635 #ifdef DEBUG
636 char buffer[100];
637 #endif
638
639 cur = form->fields[field];
640 str = cur->buffers[0].string;
641 flen = cur->cols;
642 slen = 0;
643 start = 0;
644 end = 0;
645
646 wmove(form->subwin, (int) cur->form_row, (int) cur->form_col);
647 for (row = 0; row <= cur->row_count; row++) {
648 if ((str[end] == '\0') || (str[end + 1] == '\0') || (row == 0))
649 start = end;
650 else
651 start = end + 1;
652
653 if (cur->buffers[0].length > 0) {
654 end = _formi_find_eol(str, start);
655 slen = end - start + 1;
656 } else
657 slen = 0;
658
659 switch (cur->justification) {
660 case JUSTIFY_RIGHT:
661 post = 0;
662 if (flen < slen)
663 pre = 0;
664 else
665 pre = flen - slen;
666
667 break;
668
669 case JUSTIFY_CENTER:
670 if (flen < slen) {
671 pre = 0;
672 post = 0;
673 } else {
674 pre = flen - slen;
675 post = pre = pre / 2;
676 /* get padding right if centring is not even */
677 if ((post + pre + slen) < flen)
678 post++;
679 }
680 break;
681
682 case NO_JUSTIFICATION:
683 case JUSTIFY_LEFT:
684 default:
685 pre = 0;
686 if (flen <= slen)
687 post = 0;
688 else {
689 post = flen - slen;
690 if (post > flen)
691 post = flen;
692 }
693 break;
694 }
695
696 if (pre > cur->hscroll - start)
697 pre = pre - cur->hscroll + start;
698 else
699 pre = 0;
700
701 if (slen > cur->hscroll) {
702 slen -= cur->hscroll;
703 post += cur->hscroll;
704 if (post > flen)
705 post = flen;
706 } else {
707 slen = 0;
708 post = flen - pre;
709 }
710
711 if (form->cur_field == field)
712 wattrset(form->subwin, cur->fore);
713 else
714 wattrset(form->subwin, cur->back);
715
716 #ifdef DEBUG
717 fprintf(stderr, "redraw_field: start=%d, pre=%d, slen=%d, flen=%d, post=%d, hscroll=%d\n",
718 start, pre, slen, flen, post, cur->hscroll);
719 strncpy(buffer, &str[cur->start_char], flen);
720 buffer[flen] = '\0';
721 fprintf(stderr, "redraw_field: %s\n", buffer);
722 #endif
723
724 for (i = start + cur->hscroll; i < pre; i++)
725 waddch(form->subwin, cur->pad);
726
727 offset = cur->hscroll;
728 if (cur->start_char > 0)
729 offset += cur->start_char - 1;
730
731 if (flen > cur->hscroll + 1)
732 flen -= cur->hscroll + 1;
733 else
734 flen = 0;
735
736 #ifdef DEBUG
737 fprintf(stderr, "redraw_field: will add %d chars, offset is %d\n",
738 min(slen, flen), offset);
739 #endif
740 for (i = 0;
741 i < min(slen, flen); i++)
742 {
743 #ifdef DEBUG
744 fprintf(stderr, "adding char str[%d]=%c\n",
745 i + offset, str[i + offset]);
746 #endif
747 waddch(form->subwin,
748 ((cur->opts & O_PUBLIC) == O_PUBLIC)?
749 str[i + offset] : cur->pad);
750 }
751
752 for (i = 0; i < post; i++)
753 waddch(form->subwin, cur->pad);
754 }
755
756 return;
757 }
758
759 /*
760 * Display the fields attached to the form that are on the current page
761 * on the screen.
762 *
763 */
764 int
765 _formi_draw_page(FORM *form)
766 {
767 int i;
768
769 if (form->page_starts[form->page].in_use == 0)
770 return E_BAD_ARGUMENT;
771
772 wclear(form->subwin);
773
774 for (i = form->page_starts[form->page].first;
775 i <= form->page_starts[form->page].last; i++)
776 _formi_redraw_field(form, i);
777
778 return E_OK;
779 }
780
781 /*
782 * Add the character c at the position pos in buffer 0 of the given field
783 */
784 int
785 _formi_add_char(FIELD *field, unsigned int pos, char c)
786 {
787 char *new;
788 unsigned int new_size;
789 int status;
790
791 #ifdef DEBUG
792 fprintf(stderr,"add_char: pos=%d, char=%c\n", pos, c);
793 fprintf(stderr,"add_char enter: xpos=%d, start=%d, length=%d(%d), allocated=%d\n",
794 field->cursor_xpos, field->start_char,
795 field->buffers[0].length, strlen(field->buffers[0].string),
796 field->buffers[0].allocated);
797 fprintf(stderr,"add_char enter: %s\n", field->buffers[0].string);
798 #endif
799 if (((field->opts & O_BLANK) == O_BLANK) &&
800 (field->buf0_status == FALSE)) {
801 field->buffers[0].length = 0;
802 field->buffers[0].string[0] = '\0';
803 pos = 0;
804 field->start_char = 0;
805 field->start_line = 0;
806 field->hscroll = 0;
807 field->row_count = 0;
808 field->cursor_xpos = 0;
809 field->cursor_ypos = 0;
810 }
811
812
813 if ((field->overlay == 0)
814 || ((field->overlay == 1) && (pos >= field->buffers[0].length))) {
815 if (field->buffers[0].length + 1
816 >= field->buffers[0].allocated) {
817 new_size = field->buffers[0].allocated + 64
818 - (field->buffers[0].allocated % 64);
819 if ((new = (char *) realloc(field->buffers[0].string,
820 new_size )) == NULL)
821 return E_SYSTEM_ERROR;
822 field->buffers[0].allocated = new_size;
823 field->buffers[0].string = new;
824 }
825 }
826
827 if ((field->overlay == 0) && (field->buffers[0].length > pos)) {
828 bcopy(&field->buffers[0].string[pos],
829 &field->buffers[0].string[pos + 1],
830 field->buffers[0].length - pos + 1);
831 }
832
833 field->buffers[0].string[pos] = c;
834 if (pos >= field->buffers[0].length) {
835 /* make sure the string is terminated if we are at the
836 * end of the string, the terminator would be missing
837 * if we are are at the end of the field.
838 */
839 field->buffers[0].string[pos + 1] = '\0';
840 }
841
842 /* only increment the length if we are inserting characters
843 * OR if we are at the end of the field in overlay mode.
844 */
845 if ((field->overlay == 0)
846 || ((field->overlay == 1) && (pos >= field->buffers[0].length)))
847 field->buffers[0].length++;
848
849 /* wrap the field, if needed */
850 status = _formi_wrap_field(field, pos);
851 if (status != E_OK) {
852 /* wrap failed for some reason, back out the char insert */
853 bcopy(&field->buffers[0].string[pos + 1],
854 &field->buffers[0].string[pos],
855 field->buffers[0].length - pos);
856 field->buffers[0].length--;
857 } else {
858 field->buf0_status = TRUE;
859 field->cursor_xpos++;
860 if (field->cursor_xpos >= field->cols - 1) {
861 field->start_char++;
862 field->cursor_xpos = field->cols - 1;
863 }
864 }
865
866 #ifdef DEBUG
867 fprintf(stderr,"add_char exit: xpos=%d, start=%d, length=%d(%d), allocated=%d\n",
868 field->cursor_xpos, field->start_char,
869 field->buffers[0].length, strlen(field->buffers[0].string),
870 field->buffers[0].allocated);
871 fprintf(stderr,"add_char exit: %s\n", field->buffers[0].string);
872 fprintf(stderr, "add_char exit: status = %s\n",
873 (status == E_OK)? "OK" : "FAILED");
874 #endif
875 return (status == E_OK);
876 }
877
878 /*
879 * Manipulate the text in a field, this takes the given form and performs
880 * the passed driver command on the current text field. Returns 1 if the
881 * text field was modified.
882 */
883 int
884 _formi_manipulate_field(FORM *form, int c)
885 {
886 FIELD *cur;
887 char *str;
888 unsigned int i, start, end, pos;
889
890 cur = form->fields[form->cur_field];
891
892 #ifdef DEBUG
893 fprintf(stderr, "entry: xpos=%d, start_char=%d, length=%d, allocated=%d\n",
894 cur->cursor_xpos, cur->start_char, cur->buffers[0].length,
895 cur->buffers[0].allocated);
896 fprintf(stderr, "entry: string=\"%s\"\n", cur->buffers[0].string);
897 #endif
898 switch (c) {
899 case REQ_NEXT_CHAR:
900 if ((cur->cursor_xpos + cur->start_char
901 - ((cur->start_char > 0)? 1 : 0) + cur->hscroll + 1)
902 > cur->buffers[0].length) {
903 return E_REQUEST_DENIED;
904 }
905 cur->cursor_xpos++;
906 if (cur->cursor_xpos >= cur->cols - cur->hscroll - 1) {
907 if (cur->cols < (cur->hscroll + 1))
908 cur->cursor_xpos = 0;
909 else
910 cur->cursor_xpos = cur->cols
911 - cur->hscroll - 1;
912 cur->start_char++;
913 }
914 break;
915
916 case REQ_PREV_CHAR:
917 if (cur->cursor_xpos == 0) {
918 if (cur->start_char > 0)
919 cur->start_char--;
920 else if (cur->hscroll > 0)
921 cur->hscroll--;
922 else
923 return E_REQUEST_DENIED;
924 } else
925 cur->cursor_xpos--;
926 break;
927
928 case REQ_NEXT_LINE:
929 cur->cursor_ypos++;
930 if (cur->cursor_ypos > cur->rows) {
931 if ((cur->opts & O_STATIC) == O_STATIC) {
932 if (cur->start_line + cur->cursor_ypos
933 > cur->drows) {
934 cur->cursor_ypos--;
935 return E_REQUEST_DENIED;
936 }
937 } else {
938 if (cur->start_line + cur->cursor_ypos
939 > cur->nrows + cur->rows) {
940 cur->cursor_ypos--;
941 return E_REQUEST_DENIED;
942 }
943 }
944 cur->start_line++;
945 }
946 break;
947
948 case REQ_PREV_LINE:
949 if (cur->cursor_ypos == 0) {
950 if (cur->start_line == 0)
951 return E_REQUEST_DENIED;
952 cur->start_line--;
953 } else
954 cur->cursor_ypos--;
955 break;
956
957 case REQ_NEXT_WORD:
958 start = cur->start_char + cur->cursor_xpos;
959 str = cur->buffers[0].string;
960
961 start = find_eow(str, start);
962
963 /* check if we hit the end */
964 if (str[start] == '\0')
965 return E_REQUEST_DENIED;
966
967 /* otherwise we must have found the start of a word...*/
968 if (start - cur->start_char < cur->cols) {
969 cur->cursor_xpos = start;
970 } else {
971 cur->start_char = start;
972 cur->cursor_xpos = 0;
973 }
974 break;
975
976 case REQ_PREV_WORD:
977 start = cur->start_char + cur->cursor_xpos;
978 if (cur->start_char > 0)
979 start--;
980
981 if (start == 0)
982 return E_REQUEST_DENIED;
983
984 str = cur->buffers[0].string;
985
986 start = find_sow(str, start);
987
988 if (start - cur->start_char > 0) {
989 cur->cursor_xpos = start;
990 } else {
991 cur->start_char = start;
992 cur->cursor_xpos = 0;
993 }
994 break;
995
996 case REQ_BEG_FIELD:
997 cur->start_char = 0;
998 cur->start_line = 0;
999 cur->cursor_xpos = 0;
1000 cur->cursor_ypos = 0;
1001 break;
1002
1003 case REQ_END_FIELD:
1004 if (cur->row_count > cur->rows) {
1005 cur->start_line = cur->row_count - cur->rows;
1006 cur->cursor_ypos = cur->rows - 1;
1007 } else {
1008 cur->start_line = 0;
1009 cur->cursor_ypos = cur->row_count - 1;
1010 }
1011
1012 if ((str = rindex(cur->buffers[0].string, '\n')) == NULL) {
1013 cur->cursor_xpos = cur->cols - 1;
1014 if (cur->start_char < (cur->buffers[0].length +
1015 cur->cols)) {
1016 cur->start_char = 0;
1017 cur->cursor_xpos = cur->buffers[0].length;
1018 } else {
1019 cur->start_char = cur->buffers[0].length -
1020 cur->cols;
1021 }
1022 } else {
1023 cur->start_char = (str - cur->buffers[0].string);
1024 if (strlen(str) > cur->cols)
1025 cur->cursor_xpos = cur->cols;
1026 else
1027 cur->cursor_xpos = strlen(str);
1028 }
1029 break;
1030
1031 case REQ_BEG_LINE:
1032 start = cur->start_char + cur->cursor_xpos;
1033 if (cur->buffers[0].string[start] == '\n') {
1034 if (start > 0)
1035 start--;
1036 else
1037 return E_REQUEST_DENIED;
1038 }
1039
1040 while ((start > 0)
1041 && (cur->buffers[0].string[start] != '\n'))
1042 start--;
1043
1044 if (start > 0)
1045 start++;
1046
1047 cur->start_char = start;
1048 cur->cursor_xpos = 0;
1049 break;
1050
1051 case REQ_END_LINE:
1052 start = cur->start_char + cur->cursor_xpos;
1053 end = _formi_find_eol(cur->buffers[0].string, start);
1054 start = _formi_find_bol(cur->buffers[0].string, start);
1055
1056 if (end - start > cur->cols - 1) {
1057 cur->cursor_xpos = cur->cols - 1;
1058 cur->start_char = end - cur->cols + 3;
1059 } else {
1060 cur->cursor_xpos = end - start + 1;
1061 cur->start_char = start;
1062 }
1063 break;
1064
1065 case REQ_LEFT_CHAR:
1066 if ((cur->cursor_xpos == 0) && (cur->start_char == 0))
1067 return E_REQUEST_DENIED;
1068
1069 if (cur->cursor_xpos == 0) {
1070 cur->start_char--;
1071 if (cur->buffers[0].string[cur->start_char] == '\n') {
1072 if ((cur->cursor_ypos == 0) &&
1073 (cur->start_line == 0))
1074 {
1075 cur->start_char++;
1076 return E_REQUEST_DENIED;
1077 }
1078
1079 if (cur->cursor_ypos == 0)
1080 cur->start_line--;
1081 else
1082 cur->cursor_ypos--;
1083
1084 end = _formi_find_eol(cur->buffers[0].string,
1085 cur->start_char);
1086 start = _formi_find_bol(cur->buffers[0].string,
1087 cur->start_char);
1088 if (end - start >= cur->cols) {
1089 cur->cursor_xpos = cur->cols - 1;
1090 cur->start_char = end - cur->cols;
1091 } else {
1092 cur->cursor_xpos = end - start;
1093 cur->start_char = start;
1094 }
1095 }
1096 } else
1097 cur->cursor_xpos--;
1098 break;
1099
1100 case REQ_RIGHT_CHAR:
1101 pos = cur->start_char + cur->cursor_xpos;
1102 if (cur->buffers[0].string[pos] == '\0')
1103 return E_REQUEST_DENIED;
1104
1105 #ifdef DEBUG
1106 fprintf(stderr, "req_right_char enter: start=%d, xpos=%d, c=%c\n",
1107 cur->start_char, cur->cursor_xpos,
1108 cur->buffers[0].string[pos]);
1109 #endif
1110
1111 if (cur->buffers[0].string[pos] == '\n') {
1112 start = pos + 1;
1113 if (cur->buffers[0].string[start] == 0)
1114 return E_REQUEST_DENIED;
1115 end = _formi_find_eol(cur->buffers[0].string, start);
1116 if (end - start > cur->cols) {
1117 cur->cursor_xpos = cur->cols - 1;
1118 cur->start_char = end - cur->cols - 1;
1119 } else {
1120 cur->cursor_xpos = end - start;
1121 cur->start_char = start;
1122 }
1123 } else {
1124 if (cur->cursor_xpos == cur->cols - 1)
1125 cur->start_char++;
1126 else
1127 cur->cursor_xpos++;
1128 }
1129 #ifdef DEBUG
1130 fprintf(stderr, "req_right_char exit: start=%d, xpos=%d, c=%c\n",
1131 cur->start_char, cur->cursor_xpos,
1132 cur->buffers[0].string[cur->start_char +
1133 cur->cursor_xpos]);
1134 #endif
1135 break;
1136
1137 case REQ_UP_CHAR:
1138 if (cur->cursor_ypos == 0) {
1139 if (cur->start_line == 0)
1140 return E_REQUEST_DENIED;
1141
1142 cur->start_line--;
1143 } else
1144 cur->cursor_ypos--;
1145
1146 start = find_cur_line(cur);
1147 end = _formi_find_eol(cur->buffers[0].string, start);
1148 cur->start_char = start;
1149 if (cur->cursor_xpos > end - start)
1150 cur->cursor_xpos = end - start;
1151 break;
1152
1153 case REQ_DOWN_CHAR:
1154 if (cur->cursor_ypos == cur->rows - 1) {
1155 if (cur->start_line + cur->rows == cur->row_count)
1156 return E_REQUEST_DENIED;
1157 cur->start_line++;
1158 } else
1159 cur->cursor_ypos++;
1160
1161 start = find_cur_line(cur);
1162 end = _formi_find_eol(cur->buffers[0].string, start);
1163 cur->start_char = start;
1164 if (cur->cursor_xpos > end - start)
1165 cur->cursor_xpos = end - start;
1166 break;
1167
1168 case REQ_NEW_LINE:
1169 _formi_add_char(cur, cur->start_char + cur->cursor_xpos, '\n');
1170 cur->row_count++;
1171 break;
1172
1173 case REQ_INS_CHAR:
1174 _formi_add_char(cur, cur->start_char + cur->cursor_xpos,
1175 cur->pad);
1176 break;
1177
1178 case REQ_INS_LINE:
1179 start = _formi_find_bol(cur->buffers[0].string, cur->start_char);
1180 _formi_add_char(cur, start, '\n');
1181 cur->row_count++;
1182 break;
1183
1184 case REQ_DEL_CHAR:
1185 if (cur->buffers[0].string[cur->start_char + cur->cursor_xpos]
1186 == '\0')
1187 return E_REQUEST_DENIED;
1188
1189 start = cur->start_char + cur->cursor_xpos;
1190 end = cur->buffers[0].length;
1191 if (cur->buffers[0].string[start] == '\n') {
1192 if (cur->row_count > 0) {
1193 cur->row_count--;
1194 _formi_join_line(cur, cur->buffers[0].string,
1195 start, JOIN_NEXT);
1196 } else
1197 cur->buffers[0].string[start] = '\0';
1198 } else {
1199 bcopy(&cur->buffers[0].string[start + 1],
1200 &cur->buffers[0].string[start],
1201 (unsigned) end - start + 1);
1202 }
1203
1204 cur->buffers[0].length--;
1205 break;
1206
1207 case REQ_DEL_PREV:
1208 if ((cur->cursor_xpos == 0) && (cur->start_char == 0))
1209 return E_REQUEST_DENIED;
1210
1211 start = cur->cursor_xpos + cur->start_char;
1212 end = cur->buffers[0].length;
1213
1214 if (cur->buffers[0].string[cur->start_char + cur->cursor_xpos] == '\n') {
1215 _formi_join_line(cur, cur->buffers[0].string,
1216 cur->start_char + cur->cursor_xpos,
1217 JOIN_PREV);
1218 cur->row_count--;
1219 } else {
1220 bcopy(&cur->buffers[0].string[start],
1221 &cur->buffers[0].string[start - 1],
1222 (unsigned) end - start + 1);
1223 }
1224
1225 cur->buffers[0].length--;
1226 if ((cur->cursor_xpos == 0) && (cur->start_char > 0))
1227 cur->start_char--;
1228 else if ((cur->cursor_xpos == cur->cols - 1)
1229 && (cur->start_char > 0))
1230 cur->start_char--;
1231 else if (cur->cursor_xpos > 0)
1232 cur->cursor_xpos--;
1233
1234 break;
1235
1236 case REQ_DEL_LINE:
1237 start = cur->start_char + cur->cursor_xpos;
1238 end = _formi_find_eol(cur->buffers[0].string, start);
1239 start = _formi_find_bol(cur->buffers[0].string, start);
1240 bcopy(&cur->buffers[0].string[end + 1],
1241 &cur->buffers[0].string[start],
1242 (unsigned) cur->buffers[0].length - end + 1);
1243 if (cur->row_count > 0)
1244 cur->row_count--;
1245 break;
1246
1247 case REQ_DEL_WORD:
1248 start = cur->start_char + cur->cursor_xpos;
1249 end = find_eow(cur->buffers[0].string, start);
1250 start = find_sow(cur->buffers[0].string, start);
1251 bcopy(&cur->buffers[0].string[end + 1],
1252 &cur->buffers[0].string[start],
1253 (unsigned) cur->buffers[0].length - end + 1);
1254 cur->buffers[0].length -= end - start;
1255 break;
1256
1257 case REQ_CLR_EOL:
1258 /*XXXX this right or should we just toast the chars? */
1259 start = cur->start_char + cur->cursor_xpos;
1260 end = _formi_find_eol(cur->buffers[0].string, start);
1261 for (i = start; i < end; i++)
1262 cur->buffers[0].string[i] = cur->pad;
1263 break;
1264
1265 case REQ_CLR_EOF:
1266 for (i = cur->start_char + cur->cursor_xpos;
1267 i < cur->buffers[0].length; i++)
1268 cur->buffers[0].string[i] = cur->pad;
1269 break;
1270
1271 case REQ_CLR_FIELD:
1272 for (i = 0; i < cur->buffers[0].length; i++)
1273 cur->buffers[0].string[i] = cur->pad;
1274 break;
1275
1276 case REQ_OVL_MODE:
1277 cur->overlay = 1;
1278 break;
1279
1280 case REQ_INS_MODE:
1281 cur->overlay = 0;
1282 break;
1283
1284 case REQ_SCR_FLINE:
1285 _formi_scroll_fwd(cur, 1);
1286 break;
1287
1288 case REQ_SCR_BLINE:
1289 _formi_scroll_back(cur, 1);
1290 break;
1291
1292 case REQ_SCR_FPAGE:
1293 _formi_scroll_fwd(cur, cur->rows);
1294 break;
1295
1296 case REQ_SCR_BPAGE:
1297 _formi_scroll_back(cur, cur->rows);
1298 break;
1299
1300 case REQ_SCR_FHPAGE:
1301 _formi_scroll_fwd(cur, cur->rows / 2);
1302 break;
1303
1304 case REQ_SCR_BHPAGE:
1305 _formi_scroll_back(cur, cur->rows / 2);
1306 break;
1307
1308 case REQ_SCR_FCHAR:
1309 _formi_hscroll_fwd(cur, 1);
1310 break;
1311
1312 case REQ_SCR_BCHAR:
1313 _formi_hscroll_back(cur, 1);
1314 break;
1315
1316 case REQ_SCR_HFLINE:
1317 _formi_hscroll_fwd(cur, cur->cols);
1318 break;
1319
1320 case REQ_SCR_HBLINE:
1321 _formi_hscroll_back(cur, cur->cols);
1322 break;
1323
1324 case REQ_SCR_HFHALF:
1325 _formi_hscroll_fwd(cur, cur->cols / 2);
1326 break;
1327
1328 case REQ_SCR_HBHALF:
1329 _formi_hscroll_back(cur, cur->cols / 2);
1330 break;
1331
1332 default:
1333 return 0;
1334 }
1335
1336 #ifdef DEBUG
1337 fprintf(stderr, "exit: xpos=%d, start_char=%d, length=%d, allocated=%d\n",
1338 cur->cursor_xpos, cur->start_char, cur->buffers[0].length,
1339 cur->buffers[0].allocated);
1340 fprintf(stderr, "exit: string=\"%s\"\n", cur->buffers[0].string);
1341 #endif
1342 return 1;
1343 }
1344
1345 /*
1346 * Validate the current field. If the field validation returns success then
1347 * return E_OK otherwise return E_INVALID_FIELD.
1348 *
1349 */
1350 int
1351 _formi_validate_field(FORM *form)
1352 {
1353 FIELD *cur;
1354 int ret_val;
1355
1356
1357 if ((form == NULL) || (form->fields == NULL) ||
1358 (form->fields[0] == NULL))
1359 return E_INVALID_FIELD;
1360
1361 cur = form->fields[form->cur_field];
1362
1363 if (((form->opts & O_PASSOK) == O_PASSOK) && (cur->buf0_status = 0))
1364 return E_OK;
1365
1366 if (((form->opts & O_NULLOK) == O_NULLOK) &&
1367 (cur->buffers[0].string[0] == '\0'))
1368 return E_OK;
1369
1370 /* if there is no type then just accept the field */
1371 if (cur->type == NULL)
1372 return E_OK;
1373
1374 ret_val = E_INVALID_FIELD;
1375 _formi_do_validation(cur, cur->type, &ret_val);
1376
1377 return ret_val;
1378 }
1379
1380 /*
1381 * Perform the validation of the field, invoke all field_type validation
1382 * routines. If the field is ok then update ret_val to E_OK otherwise
1383 * ret_val is not changed.
1384 */
1385 static void
1386 _formi_do_validation(FIELD *field, FIELDTYPE *type, int *ret_val)
1387 {
1388 if ((type->flags & _TYPE_IS_LINKED) == _TYPE_IS_LINKED) {
1389 _formi_do_validation(field, type->link->next, ret_val);
1390 _formi_do_validation(field, type->link->prev, ret_val);
1391 } else {
1392 if (type->field_check == NULL)
1393 *ret_val = E_OK;
1394 else {
1395 if (type->field_check(field, field_buffer(field, 0))
1396 == TRUE)
1397 *ret_val = E_OK;
1398 }
1399 }
1400 }
1401
1402 /*
1403 * Select the next/previous choice for the field, the driver command
1404 * selecting the direction will be passed in c. Return 1 if a choice
1405 * selection succeeded, 0 otherwise.
1406 */
1407 int
1408 _formi_field_choice(FORM *form, int c)
1409 {
1410 FIELDTYPE *type;
1411 FIELD *field;
1412
1413 if ((form == NULL) || (form->fields == NULL) ||
1414 (form->fields[0] == NULL) ||
1415 (form->fields[form->cur_field]->type == NULL))
1416 return 0;
1417
1418 field = form->fields[form->cur_field];
1419 type = field->type;
1420
1421 switch (c) {
1422 case REQ_NEXT_CHOICE:
1423 if (type->next_choice == NULL)
1424 return 0;
1425 else
1426 return type->next_choice(field,
1427 field_buffer(field, 0));
1428
1429 case REQ_PREV_CHOICE:
1430 if (type->prev_choice == NULL)
1431 return 0;
1432 else
1433 return type->prev_choice(field,
1434 field_buffer(field, 0));
1435
1436 default: /* should never happen! */
1437 return 0;
1438 }
1439 }
1440
1441 /*
1442 * Update the fields if they have changed. The parameter old has the
1443 * previous current field as the current field may have been updated by
1444 * the driver. Return 1 if the form page needs updating.
1445 *
1446 */
1447 int
1448 _formi_update_field(FORM *form, int old_field)
1449 {
1450 int cur, i;
1451
1452 cur = form->cur_field;
1453
1454 if (old_field != cur) {
1455 if (!((cur >= form->page_starts[form->page].first) &&
1456 (cur <= form->page_starts[form->page].last))) {
1457 /* not on same page any more */
1458 for (i = 0; i < form->max_page; i++) {
1459 if ((form->page_starts[i].in_use == 1) &&
1460 (form->page_starts[i].first <= cur) &&
1461 (form->page_starts[i].last >= cur)) {
1462 form->page = i;
1463 return 1;
1464 }
1465 }
1466 }
1467 }
1468
1469 _formi_redraw_field(form, old_field);
1470 _formi_redraw_field(form, form->cur_field);
1471 return 0;
1472 }
1473
1474 /*
1475 * Compare function for the field sorting
1476 *
1477 */
1478 static int
1479 field_sort_compare(const void *one, const void *two)
1480 {
1481 const FIELD *a, *b;
1482 int tl;
1483
1484 a = (const FIELD *) *((const FIELD **) one);
1485 b = (const FIELD *) *((const FIELD **) two);
1486
1487 if (a == NULL)
1488 return 1;
1489
1490 if (b == NULL)
1491 return -1;
1492
1493 /*
1494 * First check the page, we want the fields sorted by page.
1495 *
1496 */
1497 if (a->page != b->page)
1498 return ((a->page > b->page)? 1 : -1);
1499
1500 tl = _formi_top_left(a->parent, a->index, b->index);
1501
1502 /*
1503 * sort fields left to right, top to bottom so the top left is
1504 * the less than value....
1505 */
1506 return ((tl == a->index)? -1 : 1);
1507 }
1508
1509 /*
1510 * Sort the fields in a form ready for driver traversal.
1511 */
1512 void
1513 _formi_sort_fields(FORM *form)
1514 {
1515 FIELD **sort_area;
1516 int i;
1517
1518 CIRCLEQ_INIT(&form->sorted_fields);
1519
1520 if ((sort_area = (FIELD **) malloc(sizeof(FIELD *) * form->field_count))
1521 == NULL)
1522 return;
1523
1524 bcopy(form->fields, sort_area, sizeof(FIELD *) * form->field_count);
1525 qsort(sort_area, (unsigned) form->field_count, sizeof(FIELD *),
1526 field_sort_compare);
1527
1528 for (i = 0; i < form->field_count; i++)
1529 CIRCLEQ_INSERT_TAIL(&form->sorted_fields, sort_area[i], glue);
1530
1531 free(sort_area);
1532 }
1533
1534 /*
1535 * Set the neighbours for all the fields in the given form.
1536 */
1537 void
1538 _formi_stitch_fields(FORM *form)
1539 {
1540 int above_row, below_row, end_above, end_below, cur_row, real_end;
1541 FIELD *cur, *above, *below;
1542
1543 /*
1544 * check if the sorted fields circle queue is empty, just
1545 * return if it is.
1546 */
1547 if (CIRCLEQ_EMPTY(&form->sorted_fields))
1548 return;
1549
1550 /* initially nothing is above..... */
1551 above_row = -1;
1552 end_above = TRUE;
1553 above = NULL;
1554
1555 /* set up the first field as the current... */
1556 cur = CIRCLEQ_FIRST(&form->sorted_fields);
1557 cur_row = cur->form_row;
1558
1559 /* find the first field on the next row if any */
1560 below = CIRCLEQ_NEXT(cur, glue);
1561 below_row = -1;
1562 end_below = TRUE;
1563 real_end = TRUE;
1564 while (below != CIRCLEQ_FIRST(&form->sorted_fields)) {
1565 if (below->form_row != cur_row) {
1566 below_row = below->form_row;
1567 end_below = FALSE;
1568 real_end = FALSE;
1569 break;
1570 }
1571 below = CIRCLEQ_NEXT(below, glue);
1572 }
1573
1574 /* walk the sorted fields, setting the neighbour pointers */
1575 while (cur != (void *) &form->sorted_fields) {
1576 if (cur == CIRCLEQ_FIRST(&form->sorted_fields))
1577 cur->left = NULL;
1578 else
1579 cur->left = CIRCLEQ_PREV(cur, glue);
1580
1581 if (cur == CIRCLEQ_LAST(&form->sorted_fields))
1582 cur->right = NULL;
1583 else
1584 cur->right = CIRCLEQ_NEXT(cur, glue);
1585
1586 if (end_above == TRUE)
1587 cur->up = NULL;
1588 else {
1589 cur->up = above;
1590 above = CIRCLEQ_NEXT(above, glue);
1591 if (above_row != above->form_row) {
1592 end_above = TRUE;
1593 above_row = above->form_row;
1594 }
1595 }
1596
1597 if (end_below == TRUE)
1598 cur->down = NULL;
1599 else {
1600 cur->down = below;
1601 below = CIRCLEQ_NEXT(below, glue);
1602 if (below == (void *) &form->sorted_fields) {
1603 end_below = TRUE;
1604 real_end = TRUE;
1605 } else if (below_row != below->form_row) {
1606 end_below = TRUE;
1607 below_row = below->form_row;
1608 }
1609 }
1610
1611 cur = CIRCLEQ_NEXT(cur, glue);
1612 if ((cur != (void *) &form->sorted_fields)
1613 && (cur_row != cur->form_row)) {
1614 cur_row = cur->form_row;
1615 if (end_above == FALSE) {
1616 for (; above != CIRCLEQ_FIRST(&form->sorted_fields);
1617 above = CIRCLEQ_NEXT(above, glue)) {
1618 if (above->form_row != above_row) {
1619 above_row = above->form_row;
1620 break;
1621 }
1622 }
1623 } else if (above == NULL) {
1624 above = CIRCLEQ_FIRST(&form->sorted_fields);
1625 end_above = FALSE;
1626 above_row = above->form_row;
1627 } else
1628 end_above = FALSE;
1629
1630 if (end_below == FALSE) {
1631 while (below_row == below->form_row) {
1632 below = CIRCLEQ_NEXT(below,
1633 glue);
1634 if (below ==
1635 (void *)&form->sorted_fields) {
1636 real_end = TRUE;
1637 end_below = TRUE;
1638 break;
1639 }
1640 }
1641
1642 if (below != (void *)&form->sorted_fields)
1643 below_row = below->form_row;
1644 } else if (real_end == FALSE)
1645 end_below = FALSE;
1646
1647 }
1648 }
1649 }
1650