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