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