internals.c revision 1.18 1 /* $NetBSD: internals.c,v 1.18 2001/05/25 13:46:15 blymn Exp $ */
2
3 /*-
4 * Copyright (c) 1998-1999 Brett Lymn
5 * (blymn (at) baea.com.au, brett_lymn (at) yahoo.com.au)
6 * All rights reserved.
7 *
8 * This code has been donated to The NetBSD Foundation by the Author.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 *
30 */
31
32 #include <limits.h>
33 #include <ctype.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <strings.h>
37 #include <assert.h>
38 #include "internals.h"
39 #include "form.h"
40
41 #ifdef DEBUG
42 /*
43 * file handle to write debug info to, this will be initialised when
44 * the form is first posted.
45 */
46 FILE *dbg = NULL;
47 #endif
48
49 /* define our own min function - this is not generic but will do here
50 * (don't believe me? think about what value you would get
51 * from min(x++, y++)
52 */
53 #define min(a,b) (((a) > (b))? (b) : (a))
54
55 /* for the line joining function... */
56 #define JOIN_NEXT 1
57 #define JOIN_NEXT_NW 2 /* next join, don't wrap the joined line */
58 #define JOIN_PREV 3
59 #define JOIN_PREV_NW 4 /* previous join, don't wrap the joined line */
60
61 /* for the bump_lines function... */
62 #define _FORMI_USE_CURRENT -1 /* indicates current cursor pos to be used */
63
64 static void
65 _formi_do_char_validation(FIELD *field, FIELDTYPE *type, char c, int *ret_val);
66 static void
67 _formi_do_validation(FIELD *field, FIELDTYPE *type, int *ret_val);
68 static int
69 _formi_join_line(FIELD *field, unsigned int pos, int direction);
70 void
71 _formi_hscroll_back(FIELD *field, unsigned int amt);
72 void
73 _formi_hscroll_fwd(FIELD *field, unsigned int amt);
74 static void
75 _formi_scroll_back(FIELD *field, unsigned int amt);
76 static void
77 _formi_scroll_fwd(FIELD *field, unsigned int amt);
78 static int
79 find_sow(char *str, unsigned int offset);
80 static int
81 find_cur_line(FIELD *cur, unsigned pos);
82 static int
83 split_line(FIELD *field, unsigned pos);
84 static void
85 bump_lines(FIELD *field, int pos, int amt);
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: start_line=%d, ypos=%d\n", cur->start_line,
1217 cur->cursor_ypos);
1218 fprintf(dbg, "entry: string=");
1219 if (cur->buffers[0].string == NULL)
1220 fprintf(dbg, "(null)\n");
1221 else
1222 fprintf(dbg, "\"%s\"\n", cur->buffers[0].string);
1223 #endif
1224
1225 /* Cannot manipulate a null string! */
1226 if (cur->buffers[0].string == NULL)
1227 return E_REQUEST_DENIED;
1228
1229 switch (c) {
1230 case REQ_NEXT_CHAR:
1231 /* for a dynamic field allow an offset of one more
1232 * char so we can insert chars after end of string.
1233 * Static fields cannot do this so deny request if
1234 * cursor is at the end of the field.
1235 */
1236 if (((cur->opts & O_STATIC) == O_STATIC) &&
1237 (cur->cursor_xpos == cur->cols - 1) &&
1238 ((cur->rows + cur->nrows) == 1))
1239 return E_REQUEST_DENIED;
1240
1241 if ((cur->cursor_xpos + cur->start_char + 1)
1242 > cur->buffers[0].length)
1243 return E_REQUEST_DENIED;
1244
1245 if ((cur->rows + cur->nrows) == 1) {
1246 cur->cursor_xpos++;
1247 if (cur->cursor_xpos >= cur->cols - 1) {
1248 cur->cursor_xpos = cur->cols - 1;
1249 if ((cur->opts & O_STATIC) != O_STATIC)
1250 cur->start_char++;
1251 }
1252 } else {
1253 row = cur->start_line + cur->cursor_ypos;
1254 if (cur->cursor_xpos == (cur->lines[row].length - 1)) {
1255 if ((row + 1) >= cur->row_count)
1256 return E_REQUEST_DENIED;
1257
1258 cur->cursor_xpos = 0;
1259 if (cur->cursor_ypos == (cur->rows - 1))
1260 cur->start_line++;
1261 else
1262 cur->cursor_ypos++;
1263 } else
1264 cur->cursor_xpos++;
1265 }
1266
1267 break;
1268
1269 case REQ_PREV_CHAR:
1270 if ((cur->rows + cur->nrows) == 1) {
1271 if (cur->cursor_xpos == 0) {
1272 if (cur->start_char > 0)
1273 cur->start_char--;
1274 else
1275 return E_REQUEST_DENIED;
1276 } else
1277 cur->cursor_xpos--;
1278 } else {
1279 if ((cur->cursor_xpos == 0) &&
1280 (cur->cursor_ypos == 0) &&
1281 (cur->start_line == 0))
1282 return E_REQUEST_DENIED;
1283
1284 if (cur->cursor_xpos > 0) {
1285 cur->cursor_xpos--;
1286 } else {
1287 if (cur->cursor_ypos > 0)
1288 cur->cursor_ypos--;
1289 else
1290 cur->start_line--;
1291 cur->cursor_xpos =
1292 cur->lines[cur->start_line
1293 + cur->cursor_ypos].length
1294 - 1;
1295 }
1296 }
1297
1298 break;
1299
1300 case REQ_NEXT_LINE:
1301 cur->cursor_ypos++;
1302 if (cur->cursor_ypos > cur->rows) {
1303 if ((cur->opts & O_STATIC) == O_STATIC) {
1304 if (cur->start_line + cur->cursor_ypos
1305 > cur->drows) {
1306 cur->cursor_ypos--;
1307 return E_REQUEST_DENIED;
1308 }
1309 } else {
1310 if (cur->start_line + cur->cursor_ypos
1311 > cur->nrows + cur->rows) {
1312 cur->cursor_ypos--;
1313 return E_REQUEST_DENIED;
1314 }
1315 }
1316 cur->start_line++;
1317 }
1318 break;
1319
1320 case REQ_PREV_LINE:
1321 if (cur->cursor_ypos == 0) {
1322 if (cur->start_line == 0)
1323 return E_REQUEST_DENIED;
1324 cur->start_line--;
1325 } else
1326 cur->cursor_ypos--;
1327 break;
1328
1329 case REQ_NEXT_WORD:
1330 start = cur->lines[cur->start_line + cur->cursor_ypos].start
1331 + cur->cursor_xpos + cur->start_char;
1332 str = cur->buffers[0].string;
1333
1334 start = find_eow(str, start);
1335
1336 /* check if we hit the end */
1337 if (str[start] == '\0')
1338 return E_REQUEST_DENIED;
1339
1340 /* otherwise we must have found the start of a word...*/
1341 if ((cur->rows + cur->nrows) == 1) {
1342 /* single line field */
1343 if (start - cur->start_char < cur->cols) {
1344 cur->cursor_xpos = start;
1345 } else {
1346 cur->start_char = start;
1347 cur->cursor_xpos = 0;
1348 }
1349 } else {
1350 /* multiline field */
1351 row = find_cur_line(cur, start);
1352 cur->cursor_xpos = start - cur->lines[row].start;
1353 if (row != (cur->start_line + cur->cursor_ypos)) {
1354 if (cur->cursor_ypos == (cur->rows - 1)) {
1355 cur->start_line = row - cur->rows + 1;
1356 } else {
1357 cur->cursor_ypos = row
1358 - cur->start_line;
1359 }
1360 }
1361 }
1362 break;
1363
1364 case REQ_PREV_WORD:
1365 start = cur->start_char + cur->cursor_xpos
1366 + cur->lines[cur->start_line + cur->cursor_ypos].start;
1367 if (cur->start_char > 0)
1368 start--;
1369
1370 if (start == 0)
1371 return E_REQUEST_DENIED;
1372
1373 str = cur->buffers[0].string;
1374
1375 start = find_sow(str, start);
1376
1377 if ((cur->rows + cur->nrows) == 1) {
1378 /* single line field */
1379 if (start - cur->start_char > 0) {
1380 cur->cursor_xpos = start;
1381 } else {
1382 cur->start_char = start;
1383 cur->cursor_xpos = 0;
1384 }
1385 } else {
1386 /* multiline field */
1387 row = find_cur_line(cur, start);
1388 cur->cursor_xpos = start - cur->lines[row].start;
1389 if (row != (cur->start_line + cur->cursor_ypos)) {
1390 if (cur->cursor_ypos == 0) {
1391 cur->start_line = row;
1392 } else {
1393 if (cur->start_line > row) {
1394 cur->start_line = row;
1395 cur->cursor_ypos = 0;
1396 } else {
1397 cur->cursor_ypos = row -
1398 cur->start_line;
1399 }
1400 }
1401 }
1402 }
1403
1404 break;
1405
1406 case REQ_BEG_FIELD:
1407 cur->start_char = 0;
1408 cur->start_line = 0;
1409 cur->cursor_xpos = 0;
1410 cur->cursor_ypos = 0;
1411 break;
1412
1413 case REQ_BEG_LINE:
1414 cur->cursor_xpos = 0;
1415 break;
1416
1417 case REQ_END_FIELD:
1418 if (cur->row_count > cur->rows) {
1419 cur->start_line = cur->row_count - cur->rows;
1420 cur->cursor_ypos = cur->rows - 1;
1421 } else {
1422 cur->start_line = 0;
1423 cur->cursor_ypos = cur->row_count - 1;
1424 }
1425
1426 /* we fall through here deliberately, we are on the
1427 * correct row, now we need to get to the end of the
1428 * line.
1429 */
1430 /* FALLTHRU */
1431
1432 case REQ_END_LINE:
1433 start = cur->lines[cur->start_line + cur->cursor_ypos].start;
1434 end = cur->lines[cur->start_line + cur->cursor_ypos].end;
1435
1436 if ((cur->rows + cur->nrows) == 1) {
1437 if (end - start > cur->cols - 1) {
1438 cur->cursor_xpos = cur->cols - 1;
1439 cur->start_char = end - cur->cols;
1440 if ((cur->opts & O_STATIC) != O_STATIC)
1441 cur->start_char++;
1442 } else {
1443 cur->cursor_xpos = end - start + 1;
1444 if (((cur->opts & O_STATIC) == O_STATIC) &&
1445 ((end - start) == (cur->cols - 1)))
1446 cur->cursor_xpos--;
1447
1448 cur->start_char = start;
1449 }
1450 } else {
1451 cur->cursor_xpos = end - start + 1;
1452 }
1453 break;
1454
1455 case REQ_LEFT_CHAR:
1456 if ((cur->cursor_xpos == 0) && (cur->start_char == 0)
1457 && (cur->start_line == 0) && (cur->cursor_ypos == 0))
1458 return E_REQUEST_DENIED;
1459
1460 if (cur->cursor_xpos == 0) {
1461 if ((cur->rows + cur->nrows) == 1) {
1462 if (cur->start_char > 0)
1463 cur->start_char--;
1464 else
1465 return E_REQUEST_DENIED;
1466 } else {
1467 if ((cur->cursor_ypos == 0) &&
1468 (cur->start_line == 0))
1469 return E_REQUEST_DENIED;
1470
1471 if (cur->cursor_ypos == 0)
1472 cur->start_line--;
1473 else
1474 cur->cursor_ypos--;
1475
1476 cur->cursor_xpos =
1477 cur->lines[cur->cursor_ypos
1478 + cur->start_line].length;
1479 }
1480 } else
1481 cur->cursor_xpos--;
1482 break;
1483
1484 case REQ_RIGHT_CHAR:
1485 pos = cur->start_char + cur->cursor_xpos;
1486 row = cur->start_line + cur->cursor_ypos;
1487 end = cur->lines[row].end;
1488
1489 if (cur->buffers[0].string[pos] == '\0')
1490 return E_REQUEST_DENIED;
1491
1492 #ifdef DEBUG
1493 fprintf(dbg, "req_right_char enter: start=%d, xpos=%d, c=%c\n",
1494 cur->start_char, cur->cursor_xpos,
1495 cur->buffers[0].string[pos]);
1496 #endif
1497
1498 if (pos == end) {
1499 start = pos + 1;
1500 if ((cur->buffers[0].length <= start)
1501 || ((row + 1) >= cur->row_count))
1502 return E_REQUEST_DENIED;
1503
1504 if ((cur->cursor_ypos + 1) >= cur->rows) {
1505 cur->start_line++;
1506 cur->cursor_ypos = cur->rows - 1;
1507 } else
1508 cur->cursor_ypos++;
1509
1510 cur->cursor_xpos = 0;
1511 } else {
1512 if (((cur->rows + cur->nrows) == 1) &&
1513 (cur->cursor_xpos == cur->cols - 1))
1514 cur->start_char++;
1515 else
1516 cur->cursor_xpos++;
1517 }
1518 #ifdef DEBUG
1519 fprintf(dbg, "req_right_char exit: start=%d, xpos=%d, c=%c\n",
1520 cur->start_char, cur->cursor_xpos,
1521 cur->buffers[0].string[cur->start_char +
1522 cur->cursor_xpos]);
1523 #endif
1524 break;
1525
1526 case REQ_UP_CHAR:
1527 if (cur->cursor_ypos == 0) {
1528 if (cur->start_line == 0)
1529 return E_REQUEST_DENIED;
1530
1531 cur->start_line--;
1532 } else
1533 cur->cursor_ypos--;
1534
1535 row = cur->start_line + cur->cursor_ypos;
1536
1537 if (cur->cursor_xpos > cur->lines[row].length)
1538 cur->cursor_xpos = cur->lines[row].length;
1539 break;
1540
1541 case REQ_DOWN_CHAR:
1542 if (cur->cursor_ypos == cur->rows - 1) {
1543 if (cur->start_line + cur->rows == cur->row_count)
1544 return E_REQUEST_DENIED;
1545 cur->start_line++;
1546 } else
1547 cur->cursor_ypos++;
1548
1549 row = cur->start_line + cur->cursor_ypos;
1550 if (cur->cursor_xpos > cur->lines[row].length)
1551 cur->cursor_xpos = cur->lines[row].length;
1552 break;
1553
1554 case REQ_NEW_LINE:
1555 if ((status = split_line(cur,
1556 cur->start_char + cur->cursor_xpos)) != E_OK)
1557 return status;
1558 break;
1559
1560 case REQ_INS_CHAR:
1561 _formi_add_char(cur, cur->start_char + cur->cursor_xpos,
1562 cur->pad);
1563 break;
1564
1565 case REQ_INS_LINE:
1566 start = cur->lines[cur->start_line + cur->cursor_ypos].start;
1567 if ((status = split_line(cur, start)) != E_OK)
1568 return status;
1569 break;
1570
1571 case REQ_DEL_CHAR:
1572 if (cur->buffers[0].length == 0)
1573 return E_REQUEST_DENIED;
1574
1575 row = cur->start_line + cur->cursor_ypos;
1576 start = cur->start_char + cur->cursor_xpos;
1577 end = cur->buffers[0].length;
1578 if (start == cur->lines[row].start) {
1579 if (cur->row_count > 1) {
1580 if (_formi_join_line(cur,
1581 start, JOIN_NEXT) != E_OK) {
1582 return E_REQUEST_DENIED;
1583 }
1584 } else {
1585 cur->buffers[0].string[start] = '\0';
1586 if (cur->lines[row].end > 0) {
1587 cur->lines[row].end--;
1588 cur->lines[row].length--;
1589 }
1590 }
1591 } else {
1592 bcopy(&cur->buffers[0].string[start + 1],
1593 &cur->buffers[0].string[start],
1594 (unsigned) end - start + 1);
1595 bump_lines(cur, _FORMI_USE_CURRENT, -1);
1596 }
1597
1598 cur->buffers[0].length--;
1599 break;
1600
1601 case REQ_DEL_PREV:
1602 if ((cur->cursor_xpos == 0) && (cur->start_char == 0))
1603 return E_REQUEST_DENIED;
1604
1605 start = cur->cursor_xpos + cur->start_char;
1606 end = cur->buffers[0].length;
1607 row = cur->start_line + cur->cursor_ypos;
1608
1609 if (cur->lines[row].start ==
1610 (cur->start_char + cur->cursor_xpos)) {
1611 _formi_join_line(cur,
1612 cur->start_char + cur->cursor_xpos,
1613 JOIN_PREV);
1614 } else {
1615 bcopy(&cur->buffers[0].string[start],
1616 &cur->buffers[0].string[start - 1],
1617 (unsigned) end - start + 1);
1618 bump_lines(cur, _FORMI_USE_CURRENT, -1);
1619 }
1620
1621 cur->buffers[0].length--;
1622 if ((cur->cursor_xpos == 0) && (cur->start_char > 0))
1623 cur->start_char--;
1624 else if ((cur->cursor_xpos == cur->cols - 1)
1625 && (cur->start_char > 0))
1626 cur->start_char--;
1627 else if (cur->cursor_xpos > 0)
1628 cur->cursor_xpos--;
1629
1630 break;
1631
1632 case REQ_DEL_LINE:
1633 row = cur->start_line + cur->cursor_ypos;
1634 start = cur->lines[row].start;
1635 end = cur->lines[row].end;
1636 bcopy(&cur->buffers[0].string[end + 1],
1637 &cur->buffers[0].string[start],
1638 (unsigned) cur->buffers[0].length - end + 1);
1639 /* XXXX wrong */
1640 if (cur->row_count > 1) {
1641 cur->row_count--;
1642 bcopy(&cur->lines[row + 1], &cur->lines[row],
1643 sizeof(struct _formi_field_lines)
1644 * cur->row_count);
1645
1646 len = end - start;
1647 for (i = row; i < cur->row_count; i++) {
1648 cur->lines[i].start -= len;
1649 cur->lines[i].end -= len;
1650 }
1651
1652 if (row > cur->row_count) {
1653 if (cur->cursor_ypos == 0) {
1654 if (cur->start_line > 0) {
1655 cur->start_line--;
1656 }
1657 } else {
1658 cur->cursor_ypos--;
1659 }
1660 row--;
1661 }
1662
1663 if (cur->cursor_xpos > cur->lines[row].length)
1664 cur->cursor_xpos = cur->lines[row].length;
1665 }
1666 break;
1667
1668 case REQ_DEL_WORD:
1669 start = cur->start_char + cur->cursor_xpos;
1670 end = find_eow(cur->buffers[0].string, start);
1671 start = find_sow(cur->buffers[0].string, start);
1672 bcopy(&cur->buffers[0].string[end + 1],
1673 &cur->buffers[0].string[start],
1674 (unsigned) cur->buffers[0].length - end + 1);
1675 len = end - start;
1676 cur->buffers[0].length -= len;
1677 bump_lines(cur, _FORMI_USE_CURRENT, - (int) len);
1678
1679 if (cur->cursor_xpos > cur->lines[row].length)
1680 cur->cursor_xpos = cur->lines[row].length;
1681 break;
1682
1683 case REQ_CLR_EOL:
1684 row = cur->start_line + cur->cursor_ypos;
1685 start = cur->start_char + cur->cursor_xpos;
1686 end = cur->lines[row].end;
1687 len = end - start;
1688 bcopy(&cur->buffers[0].string[end + 1],
1689 &cur->buffers[0].string[start],
1690 cur->buffers[0].length - end + 1);
1691 cur->buffers[0].length -= len;
1692 bump_lines(cur, _FORMI_USE_CURRENT, - (int) len);
1693
1694 if (cur->cursor_xpos > cur->lines[row].length)
1695 cur->cursor_xpos = cur->lines[row].length;
1696 break;
1697
1698 case REQ_CLR_EOF:
1699 row = cur->start_line + cur->cursor_ypos;
1700 cur->buffers[0].string[cur->start_char
1701 + cur->cursor_xpos] = '\0';
1702 cur->buffers[0].length = strlen(cur->buffers[0].string);
1703 cur->lines[row].end = cur->buffers[0].length;
1704 cur->lines[row].length = cur->lines[row].end
1705 - cur->lines[row].start;
1706
1707 for (i = cur->start_char + cur->cursor_xpos;
1708 i < cur->buffers[0].length; i++)
1709 cur->buffers[0].string[i] = cur->pad;
1710 break;
1711
1712 case REQ_CLR_FIELD:
1713 cur->buffers[0].string[0] = '\0';
1714 cur->buffers[0].length = 0;
1715 cur->row_count = 1;
1716 cur->start_line = 0;
1717 cur->cursor_ypos = 0;
1718 cur->cursor_xpos = 0;
1719 cur->start_char = 0;
1720 cur->lines[0].start = 0;
1721 cur->lines[0].end = 0;
1722 cur->lines[0].length = 0;
1723 break;
1724
1725 case REQ_OVL_MODE:
1726 cur->overlay = 1;
1727 break;
1728
1729 case REQ_INS_MODE:
1730 cur->overlay = 0;
1731 break;
1732
1733 case REQ_SCR_FLINE:
1734 _formi_scroll_fwd(cur, 1);
1735 break;
1736
1737 case REQ_SCR_BLINE:
1738 _formi_scroll_back(cur, 1);
1739 break;
1740
1741 case REQ_SCR_FPAGE:
1742 _formi_scroll_fwd(cur, cur->rows);
1743 break;
1744
1745 case REQ_SCR_BPAGE:
1746 _formi_scroll_back(cur, cur->rows);
1747 break;
1748
1749 case REQ_SCR_FHPAGE:
1750 _formi_scroll_fwd(cur, cur->rows / 2);
1751 break;
1752
1753 case REQ_SCR_BHPAGE:
1754 _formi_scroll_back(cur, cur->rows / 2);
1755 break;
1756
1757 case REQ_SCR_FCHAR:
1758 _formi_hscroll_fwd(cur, 1);
1759 break;
1760
1761 case REQ_SCR_BCHAR:
1762 _formi_hscroll_back(cur, 1);
1763 break;
1764
1765 case REQ_SCR_HFLINE:
1766 _formi_hscroll_fwd(cur, cur->cols);
1767 break;
1768
1769 case REQ_SCR_HBLINE:
1770 _formi_hscroll_back(cur, cur->cols);
1771 break;
1772
1773 case REQ_SCR_HFHALF:
1774 _formi_hscroll_fwd(cur, cur->cols / 2);
1775 break;
1776
1777 case REQ_SCR_HBHALF:
1778 _formi_hscroll_back(cur, cur->cols / 2);
1779 break;
1780
1781 default:
1782 return 0;
1783 }
1784
1785 #ifdef DEBUG
1786 fprintf(dbg, "exit: xpos=%d, start_char=%d, length=%d, allocated=%d\n",
1787 cur->cursor_xpos, cur->start_char, cur->buffers[0].length,
1788 cur->buffers[0].allocated);
1789 fprintf(dbg, "exit: start_line=%d, ypos=%d\n", cur->start_line,
1790 cur->cursor_ypos);
1791 fprintf(dbg, "exit: string=\"%s\"\n", cur->buffers[0].string);
1792 #endif
1793 return 1;
1794 }
1795
1796 /*
1797 * Validate the give character by passing it to any type character
1798 * checking routines, if they exist.
1799 */
1800 int
1801 _formi_validate_char(FIELD *field, char c)
1802 {
1803 int ret_val;
1804
1805 if (field->type == NULL)
1806 return E_OK;
1807
1808 ret_val = E_INVALID_FIELD;
1809 _formi_do_char_validation(field, field->type, c, &ret_val);
1810
1811 return ret_val;
1812 }
1813
1814
1815 /*
1816 * Perform the validation of the character, invoke all field_type validation
1817 * routines. If the field is ok then update ret_val to E_OK otherwise
1818 * ret_val is not changed.
1819 */
1820 static void
1821 _formi_do_char_validation(FIELD *field, FIELDTYPE *type, char c, int *ret_val)
1822 {
1823 if ((type->flags & _TYPE_IS_LINKED) == _TYPE_IS_LINKED) {
1824 _formi_do_char_validation(field, type->link->next, c, ret_val);
1825 _formi_do_char_validation(field, type->link->prev, c, ret_val);
1826 } else {
1827 if (type->char_check == NULL)
1828 *ret_val = E_OK;
1829 else {
1830 if (type->char_check((int)(unsigned char) c,
1831 field->args) == TRUE)
1832 *ret_val = E_OK;
1833 }
1834 }
1835 }
1836
1837 /*
1838 * Validate the current field. If the field validation returns success then
1839 * return E_OK otherwise return E_INVALID_FIELD.
1840 *
1841 */
1842 int
1843 _formi_validate_field(FORM *form)
1844 {
1845 FIELD *cur;
1846 char *bp;
1847 int ret_val, count;
1848
1849
1850 if ((form == NULL) || (form->fields == NULL) ||
1851 (form->fields[0] == NULL))
1852 return E_INVALID_FIELD;
1853
1854 cur = form->fields[form->cur_field];
1855
1856 bp = cur->buffers[0].string;
1857 count = _formi_skip_blanks(bp, 0);
1858
1859 /* check if we have a null field, depending on the nullok flag
1860 * this may be acceptable or not....
1861 */
1862 if (cur->buffers[0].string[count] == '\0') {
1863 if ((cur->opts & O_NULLOK) == O_NULLOK)
1864 return E_OK;
1865 else
1866 return E_INVALID_FIELD;
1867 }
1868
1869 /* check if an unmodified field is ok */
1870 if (cur->buf0_status == 0) {
1871 if ((cur->opts & O_PASSOK) == O_PASSOK)
1872 return E_OK;
1873 else
1874 return E_INVALID_FIELD;
1875 }
1876
1877 /* if there is no type then just accept the field */
1878 if (cur->type == NULL)
1879 return E_OK;
1880
1881 ret_val = E_INVALID_FIELD;
1882 _formi_do_validation(cur, cur->type, &ret_val);
1883
1884 return ret_val;
1885 }
1886
1887 /*
1888 * Perform the validation of the field, invoke all field_type validation
1889 * routines. If the field is ok then update ret_val to E_OK otherwise
1890 * ret_val is not changed.
1891 */
1892 static void
1893 _formi_do_validation(FIELD *field, FIELDTYPE *type, int *ret_val)
1894 {
1895 if ((type->flags & _TYPE_IS_LINKED) == _TYPE_IS_LINKED) {
1896 _formi_do_validation(field, type->link->next, ret_val);
1897 _formi_do_validation(field, type->link->prev, ret_val);
1898 } else {
1899 if (type->field_check == NULL)
1900 *ret_val = E_OK;
1901 else {
1902 if (type->field_check(field, field_buffer(field, 0))
1903 == TRUE)
1904 *ret_val = E_OK;
1905 }
1906 }
1907 }
1908
1909 /*
1910 * Select the next/previous choice for the field, the driver command
1911 * selecting the direction will be passed in c. Return 1 if a choice
1912 * selection succeeded, 0 otherwise.
1913 */
1914 int
1915 _formi_field_choice(FORM *form, int c)
1916 {
1917 FIELDTYPE *type;
1918 FIELD *field;
1919
1920 if ((form == NULL) || (form->fields == NULL) ||
1921 (form->fields[0] == NULL) ||
1922 (form->fields[form->cur_field]->type == NULL))
1923 return 0;
1924
1925 field = form->fields[form->cur_field];
1926 type = field->type;
1927
1928 switch (c) {
1929 case REQ_NEXT_CHOICE:
1930 if (type->next_choice == NULL)
1931 return 0;
1932 else
1933 return type->next_choice(field,
1934 field_buffer(field, 0));
1935
1936 case REQ_PREV_CHOICE:
1937 if (type->prev_choice == NULL)
1938 return 0;
1939 else
1940 return type->prev_choice(field,
1941 field_buffer(field, 0));
1942
1943 default: /* should never happen! */
1944 return 0;
1945 }
1946 }
1947
1948 /*
1949 * Update the fields if they have changed. The parameter old has the
1950 * previous current field as the current field may have been updated by
1951 * the driver. Return 1 if the form page needs updating.
1952 *
1953 */
1954 int
1955 _formi_update_field(FORM *form, int old_field)
1956 {
1957 int cur, i;
1958
1959 cur = form->cur_field;
1960
1961 if (old_field != cur) {
1962 if (!((cur >= form->page_starts[form->page].first) &&
1963 (cur <= form->page_starts[form->page].last))) {
1964 /* not on same page any more */
1965 for (i = 0; i < form->max_page; i++) {
1966 if ((form->page_starts[i].in_use == 1) &&
1967 (form->page_starts[i].first <= cur) &&
1968 (form->page_starts[i].last >= cur)) {
1969 form->page = i;
1970 return 1;
1971 }
1972 }
1973 }
1974 }
1975
1976 _formi_redraw_field(form, old_field);
1977 _formi_redraw_field(form, form->cur_field);
1978 return 0;
1979 }
1980
1981 /*
1982 * Compare function for the field sorting
1983 *
1984 */
1985 static int
1986 field_sort_compare(const void *one, const void *two)
1987 {
1988 const FIELD *a, *b;
1989 int tl;
1990
1991 /* LINTED const castaway; we don't modify these! */
1992 a = (const FIELD *) *((const FIELD **) one);
1993 b = (const FIELD *) *((const FIELD **) two);
1994
1995 if (a == NULL)
1996 return 1;
1997
1998 if (b == NULL)
1999 return -1;
2000
2001 /*
2002 * First check the page, we want the fields sorted by page.
2003 *
2004 */
2005 if (a->page != b->page)
2006 return ((a->page > b->page)? 1 : -1);
2007
2008 tl = _formi_top_left(a->parent, a->index, b->index);
2009
2010 /*
2011 * sort fields left to right, top to bottom so the top left is
2012 * the less than value....
2013 */
2014 return ((tl == a->index)? -1 : 1);
2015 }
2016
2017 /*
2018 * Sort the fields in a form ready for driver traversal.
2019 */
2020 void
2021 _formi_sort_fields(FORM *form)
2022 {
2023 FIELD **sort_area;
2024 int i;
2025
2026 CIRCLEQ_INIT(&form->sorted_fields);
2027
2028 if ((sort_area = (FIELD **) malloc(sizeof(FIELD *) * form->field_count))
2029 == NULL)
2030 return;
2031
2032 bcopy(form->fields, sort_area, sizeof(FIELD *) * form->field_count);
2033 qsort(sort_area, (unsigned) form->field_count, sizeof(FIELD *),
2034 field_sort_compare);
2035
2036 for (i = 0; i < form->field_count; i++)
2037 CIRCLEQ_INSERT_TAIL(&form->sorted_fields, sort_area[i], glue);
2038
2039 free(sort_area);
2040 }
2041
2042 /*
2043 * Set the neighbours for all the fields in the given form.
2044 */
2045 void
2046 _formi_stitch_fields(FORM *form)
2047 {
2048 int above_row, below_row, end_above, end_below, cur_row, real_end;
2049 FIELD *cur, *above, *below;
2050
2051 /*
2052 * check if the sorted fields circle queue is empty, just
2053 * return if it is.
2054 */
2055 if (CIRCLEQ_EMPTY(&form->sorted_fields))
2056 return;
2057
2058 /* initially nothing is above..... */
2059 above_row = -1;
2060 end_above = TRUE;
2061 above = NULL;
2062
2063 /* set up the first field as the current... */
2064 cur = CIRCLEQ_FIRST(&form->sorted_fields);
2065 cur_row = cur->form_row;
2066
2067 /* find the first field on the next row if any */
2068 below = CIRCLEQ_NEXT(cur, glue);
2069 below_row = -1;
2070 end_below = TRUE;
2071 real_end = TRUE;
2072 while (below != (void *)&form->sorted_fields) {
2073 if (below->form_row != cur_row) {
2074 below_row = below->form_row;
2075 end_below = FALSE;
2076 real_end = FALSE;
2077 break;
2078 }
2079 below = CIRCLEQ_NEXT(below, glue);
2080 }
2081
2082 /* walk the sorted fields, setting the neighbour pointers */
2083 while (cur != (void *) &form->sorted_fields) {
2084 if (cur == CIRCLEQ_FIRST(&form->sorted_fields))
2085 cur->left = NULL;
2086 else
2087 cur->left = CIRCLEQ_PREV(cur, glue);
2088
2089 if (cur == CIRCLEQ_LAST(&form->sorted_fields))
2090 cur->right = NULL;
2091 else
2092 cur->right = CIRCLEQ_NEXT(cur, glue);
2093
2094 if (end_above == TRUE)
2095 cur->up = NULL;
2096 else {
2097 cur->up = above;
2098 above = CIRCLEQ_NEXT(above, glue);
2099 if (above_row != above->form_row) {
2100 end_above = TRUE;
2101 above_row = above->form_row;
2102 }
2103 }
2104
2105 if (end_below == TRUE)
2106 cur->down = NULL;
2107 else {
2108 cur->down = below;
2109 below = CIRCLEQ_NEXT(below, glue);
2110 if (below == (void *) &form->sorted_fields) {
2111 end_below = TRUE;
2112 real_end = TRUE;
2113 } else if (below_row != below->form_row) {
2114 end_below = TRUE;
2115 below_row = below->form_row;
2116 }
2117 }
2118
2119 cur = CIRCLEQ_NEXT(cur, glue);
2120 if ((cur != (void *) &form->sorted_fields)
2121 && (cur_row != cur->form_row)) {
2122 cur_row = cur->form_row;
2123 if (end_above == FALSE) {
2124 for (; above != CIRCLEQ_FIRST(&form->sorted_fields);
2125 above = CIRCLEQ_NEXT(above, glue)) {
2126 if (above->form_row != above_row) {
2127 above_row = above->form_row;
2128 break;
2129 }
2130 }
2131 } else if (above == NULL) {
2132 above = CIRCLEQ_FIRST(&form->sorted_fields);
2133 end_above = FALSE;
2134 above_row = above->form_row;
2135 } else
2136 end_above = FALSE;
2137
2138 if (end_below == FALSE) {
2139 while (below_row == below->form_row) {
2140 below = CIRCLEQ_NEXT(below,
2141 glue);
2142 if (below ==
2143 (void *)&form->sorted_fields) {
2144 real_end = TRUE;
2145 end_below = TRUE;
2146 break;
2147 }
2148 }
2149
2150 if (below != (void *)&form->sorted_fields)
2151 below_row = below->form_row;
2152 } else if (real_end == FALSE)
2153 end_below = FALSE;
2154
2155 }
2156 }
2157 }
2158