1 /* $NetBSD: internals.c,v 1.31 2006/03/19 20:08:09 christos Exp $ */
4 * Copyright (c) 1998-1999 Brett Lymn
5 * (blymn@baea.com.au, brett_lymn@yahoo.com.au)
8 * This code has been donated to The NetBSD Foundation by the Author.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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
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.
32 #include <sys/cdefs.h>
33 __RCSID("$NetBSD: internals.c,v 1.31 2006/03/19 20:08:09 christos Exp $");
41 #include "internals.h"
46 * file handle to write debug info to, this will be initialised when
47 * the form is first posted.
52 * map the request numbers to strings for debug
55 "NEXT_PAGE", "PREV_PAGE", "FIRST_PAGE", "LAST_PAGE", "NEXT_FIELD",
56 "PREV_FIELD", "FIRST_FIELD", "LAST_FIELD", "SNEXT_FIELD",
57 "SPREV_FIELD", "SFIRST_FIELD", "SLAST_FIELD", "LEFT_FIELD",
58 "RIGHT_FIELD", "UP_FIELD", "DOWN_FIELD", "NEXT_CHAR", "PREV_CHAR",
59 "NEXT_LINE", "PREV_LINE", "NEXT_WORD", "PREV_WORD", "BEG_FIELD",
60 "END_FIELD", "BEG_LINE", "END_LINE", "LEFT_CHAR", "RIGHT_CHAR",
61 "UP_CHAR", "DOWN_CHAR", "NEW_LINE", "INS_CHAR", "INS_LINE",
62 "DEL_CHAR", "DEL_PREV", "DEL_LINE", "DEL_WORD", "CLR_EOL",
63 "CLR_EOF", "CLR_FIELD", "OVL_MODE", "INS_MODE", "SCR_FLINE",
64 "SCR_BLINE", "SCR_FPAGE", "SCR_BPAGE", "SCR_FHPAGE", "SCR_BHPAGE",
65 "SCR_FCHAR", "SCR_BCHAR", "SCR_HFLINE", "SCR_HBLINE", "SCR_HFHALF",
66 "SCR_HBHALF", "VALIDATION", "PREV_CHOICE", "NEXT_CHOICE" };
69 /* define our own min function - this is not generic but will do here
70 * (don't believe me? think about what value you would get
73 #define min(a,b) (((a) > (b))? (b) : (a))
75 /* for the line joining function... */
77 #define JOIN_NEXT_NW 2 /* next join, don't wrap the joined line */
79 #define JOIN_PREV_NW 4 /* previous join, don't wrap the joined line */
81 /* for the bump_lines function... */
82 #define _FORMI_USE_CURRENT -1 /* indicates current cursor pos to be used */
84 /* used in add_char for initial memory allocation for string in row */
85 #define INITIAL_LINE_ALLOC 16
88 field_skip_blanks(unsigned int start
, _FORMI_FIELD_LINES
**rowp
);
90 _formi_do_char_validation(FIELD
*field
, FIELDTYPE
*type
, char c
, int *ret_val
);
92 _formi_do_validation(FIELD
*field
, FIELDTYPE
*type
, int *ret_val
);
94 _formi_join_line(FIELD
*field
, _FORMI_FIELD_LINES
**rowp
, int direction
);
96 _formi_hscroll_back(FIELD
*field
, _FORMI_FIELD_LINES
*row
, unsigned int amt
);
98 _formi_hscroll_fwd(FIELD
*field
, _FORMI_FIELD_LINES
*row
, unsigned int amt
);
100 _formi_scroll_back(FIELD
*field
, unsigned int amt
);
102 _formi_scroll_fwd(FIELD
*field
, unsigned int amt
);
104 _formi_set_cursor_xpos(FIELD
*field
, int no_scroll
);
106 find_sow(unsigned int offset
, _FORMI_FIELD_LINES
**rowp
);
108 split_line(FIELD
*field
, bool hard_split
, unsigned pos
,
109 _FORMI_FIELD_LINES
**rowp
);
111 check_field_size(FIELD
*field
);
113 add_tab(FORM
*form
, _FORMI_FIELD_LINES
*row
, unsigned int i
, char c
);
115 tab_size(_FORMI_FIELD_LINES
*row
, unsigned int i
);
117 tab_fit_len(_FORMI_FIELD_LINES
*row
, unsigned int len
);
119 tab_fit_window(FIELD
*field
, unsigned int pos
, unsigned int window
);
121 add_to_free(FIELD
*field
, _FORMI_FIELD_LINES
*line
);
123 adjust_ypos(FIELD
*field
, _FORMI_FIELD_LINES
*line
);
124 static _FORMI_FIELD_LINES
*
125 copy_row(_FORMI_FIELD_LINES
*row
);
127 destroy_row_list(_FORMI_FIELD_LINES
*start
);
130 * Calculate the cursor y position to make the given row appear on the
131 * field. This may be as simple as just changing the ypos (if at all) but
132 * may encompass resetting the start_line of the field to place the line
133 * at the bottom of the field. The field is assumed to be a multi-line one.
136 adjust_ypos(FIELD
*field
, _FORMI_FIELD_LINES
*line
)
139 _FORMI_FIELD_LINES
*rs
;
148 field
->cursor_ypos
= ypos
;
149 field
->start_line
= field
->lines
;
150 if (ypos
> (field
->rows
- 1)) {
152 * cur_line off the end of the field,
153 * adjust start_line so fix this.
155 field
->cursor_ypos
= field
->rows
- 1;
156 ypos
= ypos
- (field
->rows
- 1);
159 field
->start_line
= field
->start_line
->next
;
166 * Delete the given row and add it to the free list of the given field.
169 add_to_free(FIELD
*field
, _FORMI_FIELD_LINES
*line
)
171 _FORMI_FIELD_LINES
*saved
;
175 /* don't remove if only one line... */
176 if ((line
->prev
== NULL
) && (line
->next
== NULL
))
179 if (line
->prev
== NULL
) {
180 /* handle top of list */
181 field
->lines
= line
->next
;
182 field
->lines
->prev
= NULL
;
184 if (field
->cur_line
== saved
)
185 field
->cur_line
= field
->lines
;
186 if (field
->start_line
== saved
)
187 field
->start_line
= saved
;
188 } else if (line
->next
== NULL
) {
189 /* handle bottom of list */
190 line
->prev
->next
= NULL
;
191 if (field
->cur_line
== saved
)
192 field
->cur_line
= saved
->prev
;
193 if (field
->start_line
== saved
)
194 field
->cur_line
= saved
->prev
;
196 saved
->next
->prev
= saved
->prev
;
197 saved
->prev
->next
= saved
->next
;
198 if (field
->cur_line
== saved
)
199 field
->cur_line
= saved
->prev
;
200 if (field
->start_line
== saved
)
201 field
->start_line
= saved
;
204 saved
->next
= field
->free
;
207 if (saved
->next
!= NULL
)
208 saved
->next
->prev
= line
;
212 * Duplicate the given row, return the pointer to the new copy or
213 * NULL if the copy fails.
215 static _FORMI_FIELD_LINES
*
216 copy_row(_FORMI_FIELD_LINES
*row
)
218 _FORMI_FIELD_LINES
*new;
219 _formi_tab_t
*tp
, *newt
;
221 if ((new = (_FORMI_FIELD_LINES
*) malloc(sizeof(_FORMI_FIELD_LINES
)))
226 memcpy(new, row
, sizeof(_FORMI_FIELD_LINES
));
228 /* nuke the pointers from the source row so we don't get confused */
233 if ((new->string
= (char *) malloc((size_t)new->allocated
)) == NULL
) {
238 memcpy(new->string
, row
->string
, (size_t) row
->length
+ 1);
240 if (row
->tabs
!= NULL
) {
242 if ((new->tabs
= (_formi_tab_t
*) malloc(sizeof(_formi_tab_t
)))
249 memcpy(new->tabs
, row
->tabs
, sizeof(_formi_tab_t
));
250 new->tabs
->back
= NULL
;
251 new->tabs
->fwd
= NULL
;
258 (_formi_tab_t
*) malloc(sizeof(_formi_tab_t
)))
260 /* error... unwind allocations */
273 memcpy(newt
->fwd
, tp
, sizeof(_formi_tab_t
));
274 newt
->fwd
->back
= newt
;
285 * Initialise the row offset for a field, depending on the type of
286 * field it is and the type of justification used. The justification
287 * is only used on static single line fields, everything else will
288 * have the cursor_xpos set to 0.
291 _formi_init_field_xpos(FIELD
*field
)
293 /* not static or is multi-line which are not justified, so 0 it is */
294 if (((field
->opts
& O_STATIC
) != O_STATIC
) ||
295 ((field
->rows
+ field
->nrows
) != 1)) {
296 field
->cursor_xpos
= 0;
300 switch (field
->justification
) {
302 field
->cursor_xpos
= field
->cols
- 1;
306 field
->cursor_xpos
= (field
->cols
- 1) / 2;
309 default: /* assume left justify */
310 field
->cursor_xpos
= 0;
317 * Open the debug file if it is not already open....
321 _formi_create_dbg_file(void)
324 dbg
= fopen("___form_dbg.out", "w");
326 fprintf(stderr
, "Cannot open debug file!\n");
327 return E_SYSTEM_ERROR
;
336 * Check the sizing of the field, if the maximum size is set for a
337 * dynamic field then check that the number of rows or columns does
338 * not exceed the set maximum. The decision to check the rows or
339 * columns is made on the basis of how many rows are in the field -
340 * one row means the max applies to the number of columns otherwise it
341 * applies to the number of rows. If the row/column count is less
342 * than the maximum then return TRUE.
346 check_field_size(FIELD
*field
)
348 if ((field
->opts
& O_STATIC
) != O_STATIC
) {
350 if (field
->max
== 0) /* unlimited */
353 if (field
->rows
== 1) {
354 return (field
->lines
->length
< field
->max
);
356 return (field
->row_count
<= field
->max
);
359 if ((field
->rows
+ field
->nrows
) == 1) {
360 return (field
->lines
->length
<= field
->cols
);
362 return (field
->row_count
<= (field
->rows
369 * Set the form's current field to the first valid field on the page.
370 * Assume the fields have been sorted and stitched.
373 _formi_pos_first_field(FORM
*form
)
378 old_page
= form
->page
;
380 /* scan forward for an active page....*/
381 while (form
->page_starts
[form
->page
].in_use
== 0) {
383 if (form
->page
> form
->max_page
) {
384 form
->page
= old_page
;
385 return E_REQUEST_DENIED
;
389 /* then scan for a field we can use */
390 cur
= form
->fields
[form
->page_starts
[form
->page
].first
];
391 while ((cur
->opts
& (O_VISIBLE
| O_ACTIVE
))
392 != (O_VISIBLE
| O_ACTIVE
)) {
393 cur
= CIRCLEQ_NEXT(cur
, glue
);
394 if (cur
== (void *) &form
->sorted_fields
) {
395 form
->page
= old_page
;
396 return E_REQUEST_DENIED
;
400 form
->cur_field
= cur
->index
;
405 * Set the field to the next active and visible field, the fields are
406 * traversed in index order in the direction given. If the parameter
407 * use_sorted is TRUE then the sorted field list will be traversed instead
408 * of using the field index.
411 _formi_pos_new_field(FORM
*form
, unsigned direction
, unsigned use_sorted
)
417 cur
= form
->fields
[i
];
420 if (direction
== _FORMI_FORWARD
) {
421 if (use_sorted
== TRUE
) {
422 if ((form
->wrap
== FALSE
) &&
423 (cur
== CIRCLEQ_LAST(&form
->sorted_fields
)))
424 return E_REQUEST_DENIED
;
425 cur
= CIRCLEQ_NEXT(cur
, glue
);
428 if ((form
->wrap
== FALSE
) &&
429 ((i
+ 1) >= form
->field_count
))
430 return E_REQUEST_DENIED
;
432 if (i
>= form
->field_count
)
436 if (use_sorted
== TRUE
) {
437 if ((form
->wrap
== FALSE
) &&
438 (cur
== CIRCLEQ_FIRST(&form
->sorted_fields
)))
439 return E_REQUEST_DENIED
;
440 cur
= CIRCLEQ_PREV(cur
, glue
);
443 if ((form
->wrap
== FALSE
) && (i
<= 0))
444 return E_REQUEST_DENIED
;
447 i
= form
->field_count
- 1;
451 if ((form
->fields
[i
]->opts
& (O_VISIBLE
| O_ACTIVE
))
452 == (O_VISIBLE
| O_ACTIVE
)) {
457 while (i
!= form
->cur_field
);
459 return E_REQUEST_DENIED
;
463 * Destroy the list of line structs passed by freeing all allocated
467 destroy_row_list(_FORMI_FIELD_LINES
*start
)
469 _FORMI_FIELD_LINES
*temp
, *row
;
470 _formi_tab_t
*tt
, *tp
;
473 while (row
!= NULL
) {
474 if (row
->tabs
!= NULL
) {
475 /* free up the tab linked list... */
484 if (row
->string
!= NULL
)
494 * Word wrap the contents of the field's buffer 0 if this is allowed.
495 * If the wrap is successful, that is, the row count nor the buffer
496 * size is exceeded then the function will return E_OK, otherwise it
497 * will return E_REQUEST_DENIED.
500 _formi_wrap_field(FIELD
*field
, _FORMI_FIELD_LINES
*loc
)
503 unsigned int pos
, saved_xpos
, saved_ypos
, saved_cur_xpos
;
504 unsigned int saved_row_count
;
505 _FORMI_FIELD_LINES
*saved_row
, *row
, *row_backup
, *saved_cur_line
;
506 _FORMI_FIELD_LINES
*saved_start_line
, *temp
;
508 if ((field
->opts
& O_STATIC
) == O_STATIC
) {
509 if ((field
->rows
+ field
->nrows
) == 1) {
510 return E_OK
; /* cannot wrap a single line */
514 /* if we are limited to one line then don't try to wrap */
515 if ((field
->drows
+ field
->nrows
) == 1) {
520 * hueristic - if a dynamic field has more than one line
521 * on the screen then the field grows rows, otherwise
522 * it grows columns, effectively a single line field.
523 * This is documented AT&T behaviour.
525 if (field
->rows
> 1) {
534 /* if we are not at the top of the field then back up one
535 * row because we may be able to merge the current row into
538 if (row
->prev
!= NULL
)
542 saved_xpos
= field
->row_xpos
;
543 saved_cur_xpos
= field
->cursor_xpos
;
544 saved_ypos
= field
->cursor_ypos
;
545 saved_row_count
= field
->row_count
;
548 * Save a copy of the lines affected, just in case things
551 if ((row_backup
= copy_row(row
)) == NULL
)
552 return E_SYSTEM_ERROR
;
557 saved_cur_line
= temp
;
558 saved_start_line
= temp
;
560 while (row
!= NULL
) {
561 if ((temp
->next
= copy_row(row
)) == NULL
) {
562 /* a row copy failed... free up allocations */
563 destroy_row_list(row_backup
);
564 return E_SYSTEM_ERROR
;
567 temp
->next
->prev
= temp
;
570 if (row
== field
->start_line
)
571 saved_start_line
= temp
;
572 if (row
== field
->cur_line
)
573 saved_cur_line
= temp
;
579 while (row
!= NULL
) {
580 pos
= row
->length
- 1;
581 if (row
->expanded
< width
) {
582 /* line may be too short, try joining some lines */
583 if ((row
->hard_ret
== TRUE
) && (row
->next
!= NULL
)) {
585 * Skip the line if it has a hard return
586 * and it is not the last, we cannot join
593 if ((row
->next
== NULL
)) {
595 * If there are no more lines and this line
596 * is too short then our job is over.
601 if (_formi_join_line(field
, &row
,
602 JOIN_NEXT_NW
) == E_OK
) {
606 } else if (row
->expanded
> width
) {
607 /* line is too long, split it */
610 * split on first whitespace before current word
611 * if the line has tabs we need to work out where
612 * the field border lies when the tabs are expanded.
614 if (row
->tabs
== NULL
) {
616 if (pos
>= row
->expanded
)
617 pos
= row
->expanded
- 1;
619 pos
= tab_fit_len(row
, field
->cols
);
622 if ((!isblank(row
->string
[pos
])) &&
623 ((field
->opts
& O_WRAP
) == O_WRAP
)) {
624 if (!isblank(row
->string
[pos
- 1]))
625 pos
= find_sow((unsigned int) pos
,
628 * If we cannot split the line then return
629 * NO_ROOM so the driver can tell that it
630 * should not autoskip (if that is enabled)
633 || (!isblank(row
->string
[pos
- 1]))) {
634 wrap_err
= E_NO_ROOM
;
635 goto restore_and_exit
;
639 /* if we are at the end of the string and it has
640 * a trailing blank, don't wrap the blank.
642 if ((row
->next
== NULL
) && (pos
== row
->length
- 1) &&
643 (isblank(row
->string
[pos
])) &&
644 row
->expanded
<= field
->cols
)
648 * otherwise, if we are still sitting on a
649 * blank but not at the end of the line
650 * move forward one char so the blank
651 * is on the line boundary.
653 if ((isblank(row
->string
[pos
])) &&
654 (pos
!= row
->length
- 1))
657 if (split_line(field
, FALSE
, pos
, &row
) != E_OK
) {
658 wrap_err
= E_REQUEST_DENIED
;
659 goto restore_and_exit
;
662 /* line is exactly the right length, do next one */
666 /* Check if we have not run out of room */
667 if ((((field
->opts
& O_STATIC
) == O_STATIC
) &&
668 field
->row_count
> (field
->rows
+ field
->nrows
)) ||
669 ((field
->max
!= 0) && (field
->row_count
> field
->max
))) {
671 wrap_err
= E_REQUEST_DENIED
;
674 if (saved_row
->prev
== NULL
) {
675 field
->lines
= row_backup
;
677 saved_row
->prev
->next
= row_backup
;
678 row_backup
->prev
= saved_row
->prev
;
681 field
->row_xpos
= saved_xpos
;
682 field
->cursor_xpos
= saved_cur_xpos
;
683 field
->cursor_ypos
= saved_ypos
;
684 field
->row_count
= saved_row_count
;
685 field
->start_line
= saved_start_line
;
686 field
->cur_line
= saved_cur_line
;
688 destroy_row_list(saved_row
);
692 destroy_row_list(row_backup
);
697 * Join the two lines that surround the location pos, the type
698 * variable indicates the direction of the join, JOIN_NEXT will join
699 * the next line to the current line, JOIN_PREV will join the current
700 * line to the previous line, the new lines will be wrapped unless the
701 * _NW versions of the directions are used. Returns E_OK if the join
702 * was successful or E_REQUEST_DENIED if the join cannot happen.
705 _formi_join_line(FIELD
*field
, _FORMI_FIELD_LINES
**rowp
, int direction
)
708 struct _formi_field_lines
*saved
;
710 _FORMI_FIELD_LINES
*row
= *rowp
;
714 if (_formi_create_dbg_file() == E_OK
) {
718 if (dbg_ok
== TRUE
) {
719 fprintf(dbg
, "join_line: working on row %p, row_count = %d\n",
720 row
, field
->row_count
);
724 if ((direction
== JOIN_NEXT
) || (direction
== JOIN_NEXT_NW
)) {
726 * See if there is another line following, or if the
727 * line contains a hard return then we don't join
730 if ((row
->next
== NULL
) || (row
->hard_ret
== TRUE
)) {
731 return E_REQUEST_DENIED
;
735 if (dbg_ok
== TRUE
) {
737 "join_line: join_next before length = %d, expanded = %d",
738 row
->length
, row
->expanded
);
740 " :: next row length = %d, expanded = %d\n",
741 row
->length
, row
->expanded
);
745 if (row
->allocated
< (row
->length
+ row
->next
->length
+ 1)) {
746 if ((newp
= realloc(row
->string
, (size_t)(row
->length
+
749 return E_REQUEST_DENIED
;
751 row
->allocated
= row
->length
+ row
->next
->length
+ 1;
754 strcat(row
->string
, row
->next
->string
);
755 old_len
= row
->length
;
756 row
->length
+= row
->next
->length
;
759 _formi_tab_expanded_length(row
->string
, 0,
764 _formi_calculate_tabs(row
);
765 row
->hard_ret
= row
->next
->hard_ret
;
767 /* adjust current line if it is on the row being eaten */
768 if (field
->cur_line
== row
->next
) {
769 field
->cur_line
= row
;
770 field
->row_xpos
+= old_len
;
772 _formi_tab_expanded_length(row
->string
, 0,
774 if (field
->cursor_xpos
> 0)
775 field
->cursor_xpos
--;
777 if (field
->cursor_ypos
> 0)
778 field
->cursor_ypos
--;
780 if (field
->start_line
->prev
!= NULL
)
782 field
->start_line
->prev
;
786 /* remove joined line record from the row list */
787 add_to_free(field
, row
->next
);
790 if (dbg_ok
== TRUE
) {
792 "join_line: exit length = %d, expanded = %d\n",
793 row
->length
, row
->expanded
);
797 if (row
->prev
== NULL
) {
798 return E_REQUEST_DENIED
;
804 * Don't try to join if the line above has a hard
807 if (saved
->hard_ret
== TRUE
) {
808 return E_REQUEST_DENIED
;
812 if (dbg_ok
== TRUE
) {
814 "join_line: join_prev before length = %d, expanded = %d",
815 row
->length
, row
->expanded
);
817 " :: prev row length = %d, expanded = %d\n",
818 saved
->length
, saved
->expanded
);
822 if (saved
->allocated
< (row
->length
+ saved
->length
+ 1)) {
823 if ((newp
= realloc(saved
->string
,
824 (size_t) (row
->length
+
827 return E_REQUEST_DENIED
;
828 saved
->string
= newp
;
829 saved
->allocated
= row
->length
+ saved
->length
+ 1;
832 strcat(saved
->string
, row
->string
);
833 old_len
= saved
->length
;
834 saved
->length
+= row
->length
;
835 if (saved
->length
> 0)
837 _formi_tab_expanded_length(saved
->string
, 0,
842 saved
->hard_ret
= row
->hard_ret
;
844 /* adjust current line if it was on the row being eaten */
845 if (field
->cur_line
== row
) {
846 field
->cur_line
= saved
;
847 field
->row_xpos
+= old_len
;
849 _formi_tab_expanded_length(saved
->string
, 0,
851 if (field
->cursor_xpos
> 0)
852 field
->cursor_xpos
--;
855 add_to_free(field
, row
);
858 if (dbg_ok
== TRUE
) {
860 "join_line: exit length = %d, expanded = %d\n",
861 saved
->length
, saved
->expanded
);
869 * Work out where the line lies in the field in relation to
870 * the cursor_ypos. First count the rows from the start of
871 * the field until we hit the row we just worked on.
873 saved
= field
->start_line
;
875 while (saved
->next
!= NULL
) {
882 /* now check if we need to adjust cursor_ypos */
883 if (field
->cursor_ypos
> count
) {
884 field
->cursor_ypos
--;
890 /* wrap the field if required, if this fails undo the change */
891 if ((direction
== JOIN_NEXT
) || (direction
== JOIN_PREV
)) {
892 if (_formi_wrap_field(field
, row
) != E_OK
) {
893 return E_REQUEST_DENIED
;
901 * Split the line at the given position, if possible. If hard_split is
902 * TRUE then split the line regardless of the position, otherwise don't
903 * split at the beginning of a line.
906 split_line(FIELD
*field
, bool hard_split
, unsigned pos
,
907 _FORMI_FIELD_LINES
**rowp
)
909 struct _formi_field_lines
*new_line
;
911 _FORMI_FIELD_LINES
*row
= *rowp
;
913 short dbg_ok
= FALSE
;
916 /* if asked to split right where the line already starts then
917 * just return - nothing to do unless we are appending a line
920 if ((pos
== 0) && (hard_split
== FALSE
))
924 if (_formi_create_dbg_file() == E_OK
) {
925 fprintf(dbg
, "split_line: splitting line at %d\n", pos
);
930 /* Need an extra line struct, check free list first */
931 if (field
->free
!= NULL
) {
932 new_line
= field
->free
;
933 field
->free
= new_line
->next
;
934 if (field
->free
!= NULL
)
935 field
->free
->prev
= NULL
;
937 if ((new_line
= (struct _formi_field_lines
*)
938 malloc(sizeof(struct _formi_field_lines
))) == NULL
)
939 return E_SYSTEM_ERROR
;
940 new_line
->prev
= NULL
;
941 new_line
->next
= NULL
;
942 new_line
->allocated
= 0;
943 new_line
->length
= 0;
944 new_line
->expanded
= 0;
945 new_line
->string
= NULL
;
946 new_line
->hard_ret
= FALSE
;
947 new_line
->tabs
= NULL
;
951 if (dbg_ok
== TRUE
) {
953 "split_line: enter: length = %d, expanded = %d\n",
954 row
->length
, row
->expanded
);
958 assert((row
->length
< INT_MAX
) && (row
->expanded
< INT_MAX
));
961 /* add new line to the row list */
962 new_line
->next
= row
->next
;
963 new_line
->prev
= row
;
964 row
->next
= new_line
;
965 if (new_line
->next
!= NULL
)
966 new_line
->next
->prev
= new_line
;
968 new_line
->length
= row
->length
- pos
;
969 if (new_line
->length
>= new_line
->allocated
) {
970 if ((newp
= realloc(new_line
->string
,
971 (size_t) new_line
->length
+ 1)) == NULL
)
972 return E_SYSTEM_ERROR
;
973 new_line
->string
= newp
;
974 new_line
->allocated
= new_line
->length
+ 1;
977 strcpy(new_line
->string
, &row
->string
[pos
]);
980 row
->string
[pos
] = '\0';
982 if (row
->length
!= 0)
983 row
->expanded
= _formi_tab_expanded_length(row
->string
, 0,
987 _formi_calculate_tabs(row
);
989 if (new_line
->length
!= 0)
991 _formi_tab_expanded_length(new_line
->string
, 0,
992 new_line
->length
- 1);
994 new_line
->expanded
= 0;
996 _formi_calculate_tabs(new_line
);
999 * If the given row was the current line then adjust the
1000 * current line pointer if necessary
1002 if ((field
->cur_line
== row
) && (field
->row_xpos
>= pos
)) {
1003 field
->cur_line
= new_line
;
1004 field
->row_xpos
-= pos
;
1005 field
->cursor_xpos
=
1006 _formi_tab_expanded_length(new_line
->string
, 0,
1008 if (field
->cursor_xpos
> 0)
1009 field
->cursor_xpos
--;
1011 field
->cursor_ypos
++;
1012 if (field
->cursor_ypos
>= field
->rows
) {
1013 if (field
->start_line
->next
!= NULL
) {
1014 field
->start_line
= field
->start_line
->next
;
1015 field
->cursor_ypos
= field
->rows
- 1;
1018 assert(field
->start_line
->next
== NULL
);
1023 * If the line split had a hard return then replace the
1024 * current line's hard return with a soft return and carry
1025 * the hard return onto the line after.
1027 if (row
->hard_ret
== TRUE
) {
1028 new_line
->hard_ret
= TRUE
;
1029 row
->hard_ret
= FALSE
;
1033 * except where we are doing a hard split then the current
1034 * row must have a hard return on it too...
1036 if (hard_split
== TRUE
) {
1037 row
->hard_ret
= TRUE
;
1040 assert(((row
->expanded
< INT_MAX
) &&
1041 (new_line
->expanded
< INT_MAX
) &&
1042 (row
->length
< INT_MAX
) &&
1043 (new_line
->length
< INT_MAX
)));
1046 if (dbg_ok
== TRUE
) {
1047 fprintf(dbg
, "split_line: exit: ");
1048 fprintf(dbg
, "row.length = %d, row.expanded = %d, ",
1049 row
->length
, row
->expanded
);
1051 "next_line.length = %d, next_line.expanded = %d, ",
1052 new_line
->length
, new_line
->expanded
);
1053 fprintf(dbg
, "row_count = %d\n", field
->row_count
+ 1);
1064 * skip the blanks in the given string, start at the index start and
1065 * continue forward until either the end of the string or a non-blank
1066 * character is found. Return the index of either the end of the string or
1067 * the first non-blank character.
1070 _formi_skip_blanks(char *string
, unsigned int start
)
1076 while ((string
[i
] != '\0') && isblank(string
[i
]))
1083 * Skip the blanks in the string associated with the given row, pass back
1084 * the row and the offset at which the first non-blank is found. If no
1085 * non-blank character is found then return the index to the last
1086 * character on the last line.
1090 field_skip_blanks(unsigned int start
, _FORMI_FIELD_LINES
**rowp
)
1093 _FORMI_FIELD_LINES
*row
, *last
= NULL
;
1099 i
= _formi_skip_blanks(&row
->string
[i
], i
);
1100 if (!isblank(row
->string
[i
])) {
1104 * don't reset if last line otherwise we will
1105 * not be at the end of the string.
1112 while (row
!= NULL
);
1115 * If we hit the end of the row list then point at the last row
1116 * otherwise we return the row we found the blank on.
1127 * Return the index of the top left most field of the two given fields.
1130 _formi_top_left(FORM
*form
, int a
, int b
)
1132 /* lower row numbers always win here.... */
1133 if (form
->fields
[a
]->form_row
< form
->fields
[b
]->form_row
)
1136 if (form
->fields
[a
]->form_row
> form
->fields
[b
]->form_row
)
1139 /* rows must be equal, check columns */
1140 if (form
->fields
[a
]->form_col
< form
->fields
[b
]->form_col
)
1143 if (form
->fields
[a
]->form_col
> form
->fields
[b
]->form_col
)
1146 /* if we get here fields must be in exactly the same place, punt */
1151 * Return the index to the field that is the bottom-right-most of the
1155 _formi_bottom_right(FORM
*form
, int a
, int b
)
1157 /* check the rows first, biggest row wins */
1158 if (form
->fields
[a
]->form_row
> form
->fields
[b
]->form_row
)
1160 if (form
->fields
[a
]->form_row
< form
->fields
[b
]->form_row
)
1163 /* rows must be equal, check cols, biggest wins */
1164 if (form
->fields
[a
]->form_col
> form
->fields
[b
]->form_col
)
1166 if (form
->fields
[a
]->form_col
< form
->fields
[b
]->form_col
)
1169 /* fields in the same place, punt */
1174 * Find the end of the current word in the string str, starting at
1175 * offset - the end includes any trailing whitespace. If the end of
1176 * the string is found before a new word then just return the offset
1177 * to the end of the string. If do_join is TRUE then lines will be
1178 * joined (without wrapping) until either the end of the field or the
1179 * end of a word is found (whichever comes first).
1182 find_eow(FIELD
*cur
, unsigned int offset
, bool do_join
,
1183 _FORMI_FIELD_LINES
**rowp
)
1186 _FORMI_FIELD_LINES
*row
;
1192 /* first skip any non-whitespace */
1193 while ((row
->string
[start
] != '\0')
1194 && !isblank(row
->string
[start
]))
1197 /* see if we hit the end of the string */
1198 if (row
->string
[start
] == '\0') {
1199 if (do_join
== TRUE
) {
1200 if (row
->next
== NULL
)
1203 if (_formi_join_line(cur
, &row
, JOIN_NEXT_NW
)
1205 return E_REQUEST_DENIED
;
1208 if (row
->next
== NULL
) {
1215 } while (row
->length
== 0);
1218 } while (!isblank(row
->string
[start
]));
1221 /* otherwise skip the whitespace.... */
1222 while ((row
->string
[start
] != '\0')
1223 && isblank(row
->string
[start
]))
1226 if (row
->string
[start
] == '\0') {
1227 if (do_join
== TRUE
) {
1228 if (row
->next
== NULL
)
1231 if (_formi_join_line(cur
, &row
, JOIN_NEXT_NW
)
1233 return E_REQUEST_DENIED
;
1236 if (row
->next
== NULL
) {
1243 } while (row
->length
== 0);
1246 } while (isblank(row
->string
[start
]));
1253 * Find the beginning of the current word in the string str, starting
1257 find_sow(unsigned int offset
, _FORMI_FIELD_LINES
**rowp
)
1261 _FORMI_FIELD_LINES
*row
;
1269 if (isblank(str
[start
]) || isblank(str
[start
- 1])) {
1270 if (isblank(str
[start
- 1]))
1272 /* skip the whitespace.... */
1273 while ((start
>= 0) && isblank(str
[start
]))
1278 /* see if we hit the start of the string */
1281 if (row
->prev
== NULL
) {
1288 if (row
->length
> 0)
1289 start
= row
->length
- 1;
1293 } while (row
->length
== 0);
1295 } while (isblank(row
->string
[start
]));
1297 /* see if we hit the start of the string */
1303 /* now skip any non-whitespace */
1305 while ((start
>= 0) && !isblank(str
[start
]))
1311 if (row
->prev
== NULL
) {
1318 if (row
->length
> 0)
1319 start
= row
->length
- 1;
1323 } while (row
->length
== 0);
1325 } while (!isblank(str
[start
]));
1328 start
++; /* last loop has us pointing at a space, adjust */
1329 if (start
>= row
->length
) {
1330 if (row
->next
!= NULL
) {
1334 start
= row
->length
- 1;
1347 * Scroll the field forward the given number of lines.
1350 _formi_scroll_fwd(FIELD
*field
, unsigned int amt
)
1353 _FORMI_FIELD_LINES
*end_row
;
1355 end_row
= field
->start_line
;
1356 /* walk the line structs forward to find the bottom of the field */
1357 count
= field
->rows
- 1;
1358 while ((count
> 0) && (end_row
->next
!= NULL
))
1361 end_row
= end_row
->next
;
1364 /* check if there are lines to scroll */
1365 if ((count
> 0) && (end_row
->next
== NULL
))
1369 * ok, lines to scroll - do this by walking both the start_line
1370 * and the end_row at the same time for amt lines, we stop when
1371 * either we have done the number of lines or end_row hits the
1372 * last line in the field.
1375 while ((count
> 0) && (end_row
->next
!= NULL
)) {
1377 field
->start_line
= field
->start_line
->next
;
1378 end_row
= end_row
->next
;
1383 * Scroll the field backward the given number of lines.
1386 _formi_scroll_back(FIELD
*field
, unsigned int amt
)
1390 /* check for lines above */
1391 if (field
->start_line
->prev
== NULL
)
1395 * Backward scroll is easy, follow row struct chain backward until
1396 * the number of lines done or we reach the top of the field.
1399 while ((count
> 0) && (field
->start_line
->prev
!= NULL
)) {
1401 field
->start_line
= field
->start_line
->prev
;
1406 * Scroll the field forward the given number of characters.
1409 _formi_hscroll_fwd(FIELD
*field
, _FORMI_FIELD_LINES
*row
, int unsigned amt
)
1411 unsigned int end
, scroll_amt
, expanded
;
1415 if ((row
->tabs
== NULL
) || (row
->tabs
->in_use
== FALSE
)) {
1416 /* if the line has no tabs things are easy... */
1417 end
= field
->start_char
+ field
->cols
+ amt
- 1;
1419 if (end
> row
->length
) {
1421 scroll_amt
= end
- field
->start_char
- field
->cols
+ 1;
1425 * If there are tabs we need to add on the scroll amount,
1426 * find the last char position that will fit into
1427 * the field and finally fix up the start_char. This
1428 * is a lot of work but handling the case where there
1429 * are not enough chars to scroll by amt is difficult.
1431 end
= field
->start_char
+ field
->row_xpos
+ amt
;
1432 if (end
>= row
->length
)
1433 end
= row
->length
- 1;
1435 expanded
= _formi_tab_expanded_length(
1437 field
->start_char
+ amt
,
1438 field
->start_char
+ field
->row_xpos
+ amt
);
1440 /* skip tabs to the lhs of our starting point */
1441 while ((ts
!= NULL
) && (ts
->in_use
== TRUE
)
1445 while ((expanded
<= field
->cols
)
1446 && (end
< row
->length
)) {
1447 if (row
->string
[end
] == '\t') {
1449 && (ts
->in_use
== TRUE
));
1450 if (ts
->pos
== end
) {
1451 if ((expanded
+ ts
->size
)
1454 expanded
+= ts
->size
;
1458 assert(ts
->pos
== end
);
1465 scroll_amt
= tab_fit_window(field
, end
, field
->cols
);
1466 if (scroll_amt
< field
->start_char
)
1469 scroll_amt
-= field
->start_char
;
1471 scroll_amt
= min(scroll_amt
, amt
);
1474 field
->start_char
+= scroll_amt
;
1475 field
->cursor_xpos
=
1476 _formi_tab_expanded_length(row
->string
,
1479 + field
->start_char
) - 1;
1484 * Scroll the field backward the given number of characters.
1487 _formi_hscroll_back(FIELD
*field
, _FORMI_FIELD_LINES
*row
, unsigned int amt
)
1489 field
->start_char
-= min(field
->start_char
, amt
);
1490 field
->cursor_xpos
=
1491 _formi_tab_expanded_length(row
->string
, field
->start_char
,
1493 + field
->start_char
) - 1;
1494 if (field
->cursor_xpos
>= field
->cols
) {
1495 field
->row_xpos
= 0;
1496 field
->cursor_xpos
= 0;
1501 * Find the different pages in the form fields and assign the form
1502 * page_starts array with the information to find them.
1505 _formi_find_pages(FORM
*form
)
1507 int i
, cur_page
= 0;
1509 if ((form
->page_starts
= (_FORMI_PAGE_START
*)
1510 malloc((form
->max_page
+ 1) * sizeof(_FORMI_PAGE_START
))) == NULL
)
1511 return E_SYSTEM_ERROR
;
1513 /* initialise the page starts array */
1514 memset(form
->page_starts
, 0,
1515 (form
->max_page
+ 1) * sizeof(_FORMI_PAGE_START
));
1517 for (i
=0; i
< form
->field_count
; i
++) {
1518 if (form
->fields
[i
]->page_break
== 1)
1520 if (form
->page_starts
[cur_page
].in_use
== 0) {
1521 form
->page_starts
[cur_page
].in_use
= 1;
1522 form
->page_starts
[cur_page
].first
= i
;
1523 form
->page_starts
[cur_page
].last
= i
;
1524 form
->page_starts
[cur_page
].top_left
= i
;
1525 form
->page_starts
[cur_page
].bottom_right
= i
;
1527 form
->page_starts
[cur_page
].last
= i
;
1528 form
->page_starts
[cur_page
].top_left
=
1529 _formi_top_left(form
,
1530 form
->page_starts
[cur_page
].top_left
,
1532 form
->page_starts
[cur_page
].bottom_right
=
1533 _formi_bottom_right(form
,
1534 form
->page_starts
[cur_page
].bottom_right
,
1543 * Completely redraw the field of the given form.
1546 _formi_redraw_field(FORM
*form
, int field
)
1548 unsigned int pre
, post
, flen
, slen
, i
, j
, start
, line
;
1549 unsigned int tab
, cpos
, len
;
1552 _FORMI_FIELD_LINES
*row
;
1557 cur
= form
->fields
[field
];
1563 for (row
= cur
->start_line
; ((row
!= NULL
) && (line
< cur
->rows
));
1564 row
= row
->next
, line
++) {
1565 wmove(form
->scrwin
, (int) (cur
->form_row
+ line
),
1566 (int) cur
->form_col
);
1567 if ((cur
->rows
+ cur
->nrows
) == 1) {
1568 if ((cur
->cols
+ cur
->start_char
) >= row
->length
)
1571 len
= cur
->cols
+ cur
->start_char
;
1572 if (row
->string
!= NULL
)
1573 slen
= _formi_tab_expanded_length(
1574 row
->string
, cur
->start_char
, len
);
1578 if (slen
> cur
->cols
)
1580 slen
+= cur
->start_char
;
1582 slen
= row
->expanded
;
1584 if ((cur
->opts
& O_STATIC
) == O_STATIC
) {
1585 switch (cur
->justification
) {
1594 case JUSTIFY_CENTER
:
1600 post
= pre
= pre
/ 2;
1601 /* get padding right if
1602 centring is not even */
1603 if ((post
+ pre
+ slen
) < flen
)
1608 case NO_JUSTIFICATION
:
1622 /* dynamic fields are not justified */
1632 /* but they do scroll.... */
1634 if (pre
> cur
->start_char
- start
)
1635 pre
= pre
- cur
->start_char
+ start
;
1639 if (slen
> cur
->start_char
) {
1640 slen
-= cur
->start_char
;
1654 if (form
->cur_field
== field
)
1655 wattrset(form
->scrwin
, cur
->fore
);
1657 wattrset(form
->scrwin
, cur
->back
);
1659 str
= &row
->string
[cur
->start_char
];
1662 if (_formi_create_dbg_file() == E_OK
) {
1664 "redraw_field: start=%d, pre=%d, slen=%d, flen=%d, post=%d, start_char=%d\n",
1665 start
, pre
, slen
, flen
, post
, cur
->start_char
);
1667 if (row
->expanded
!= 0) {
1668 strncpy(buffer
, str
, flen
);
1670 strcpy(buffer
, "(empty)");
1673 strcpy(buffer
, "(null)");
1675 buffer
[flen
] = '\0';
1676 fprintf(dbg
, "redraw_field: %s\n", buffer
);
1680 for (i
= start
+ cur
->start_char
; i
< pre
; i
++)
1681 waddch(form
->scrwin
, cur
->pad
);
1684 fprintf(dbg
, "redraw_field: will add %d chars\n",
1687 for (i
= 0, cpos
= cur
->start_char
; i
< min(slen
, flen
);
1691 tab
= 0; /* just to shut gcc up */
1693 fprintf(dbg
, "adding char str[%d]=%c\n",
1694 cpos
+ cur
->start_char
, c
);
1696 if (((cur
->opts
& O_PUBLIC
) != O_PUBLIC
)) {
1698 tab
= add_tab(form
, row
, cpos
,
1701 waddch(form
->scrwin
, cur
->pad
);
1702 } else if ((cur
->opts
& O_VISIBLE
) == O_VISIBLE
) {
1704 tab
= add_tab(form
, row
, cpos
, ' ');
1706 waddch(form
->scrwin
, c
);
1709 tab
= add_tab(form
, row
, cpos
, ' ');
1711 waddch(form
->scrwin
, ' ');
1715 * If we have had a tab then skip forward
1716 * the requisite number of chars to keep
1723 for (i
= 0; i
< post
; i
++)
1724 waddch(form
->scrwin
, cur
->pad
);
1727 for (i
= line
; i
< cur
->rows
; i
++) {
1728 wmove(form
->scrwin
, (int) (cur
->form_row
+ i
),
1729 (int) cur
->form_col
);
1731 if (form
->cur_field
== field
)
1732 wattrset(form
->scrwin
, cur
->fore
);
1734 wattrset(form
->scrwin
, cur
->back
);
1736 for (j
= 0; j
< cur
->cols
; j
++) {
1737 waddch(form
->scrwin
, cur
->pad
);
1741 wattrset(form
->scrwin
, cur
->back
);
1746 * Add the correct number of the given character to simulate a tab
1750 add_tab(FORM
*form
, _FORMI_FIELD_LINES
*row
, unsigned int i
, char c
)
1753 _formi_tab_t
*ts
= row
->tabs
;
1755 while ((ts
!= NULL
) && (ts
->pos
!= i
))
1760 for (j
= 0; j
< ts
->size
; j
++)
1761 waddch(form
->scrwin
, c
);
1768 * Display the fields attached to the form that are on the current page
1773 _formi_draw_page(FORM
*form
)
1777 if (form
->page_starts
[form
->page
].in_use
== 0)
1778 return E_BAD_ARGUMENT
;
1780 wclear(form
->scrwin
);
1782 for (i
= form
->page_starts
[form
->page
].first
;
1783 i
<= form
->page_starts
[form
->page
].last
; i
++)
1784 _formi_redraw_field(form
, i
);
1790 * Add the character c at the position pos in buffer 0 of the given field
1793 _formi_add_char(FIELD
*field
, unsigned int pos
, char c
)
1796 unsigned int new_size
;
1798 _FORMI_FIELD_LINES
*row
, *temp
, *next_temp
;
1800 row
= field
->cur_line
;
1803 * If buffer has not had a string before, set it to a blank
1804 * string. Everything should flow from there....
1806 if (row
->string
== NULL
) {
1807 if ((row
->string
= (char *) malloc((size_t)INITIAL_LINE_ALLOC
))
1809 return E_SYSTEM_ERROR
;
1810 row
->string
[0] = '\0';
1811 row
->allocated
= INITIAL_LINE_ALLOC
;
1816 if (_formi_validate_char(field
, c
) != E_OK
) {
1818 fprintf(dbg
, "add_char: char %c failed char validation\n", c
);
1820 return E_INVALID_FIELD
;
1823 if ((c
== '\t') && (field
->cols
<= 8)) {
1825 fprintf(dbg
, "add_char: field too small for a tab\n");
1831 fprintf(dbg
, "add_char: pos=%d, char=%c\n", pos
, c
);
1832 fprintf(dbg
, "add_char enter: xpos=%d, row_pos=%d, start=%d\n",
1833 field
->cursor_xpos
, field
->row_xpos
, field
->start_char
);
1834 fprintf(dbg
, "add_char enter: length=%d(%d), allocated=%d\n",
1835 row
->expanded
, row
->length
, row
->allocated
);
1836 fprintf(dbg
, "add_char enter: %s\n", row
->string
);
1837 fprintf(dbg
, "add_char enter: buf0_status=%d\n", field
->buf0_status
);
1839 if (((field
->opts
& O_BLANK
) == O_BLANK
) &&
1840 (field
->buf0_status
== FALSE
) &&
1841 ((field
->row_xpos
+ field
->start_char
) == 0)) {
1843 if (row
->next
!= NULL
) {
1844 /* shift all but one line structs to free list */
1847 next_temp
= temp
->next
;
1848 add_to_free(field
, temp
);
1850 } while (temp
!= NULL
);
1854 row
->string
[0] = '\0';
1856 field
->start_char
= 0;
1857 field
->start_line
= row
;
1858 field
->cur_line
= row
;
1859 field
->row_count
= 1;
1860 field
->row_xpos
= 0;
1861 field
->cursor_ypos
= 0;
1864 _formi_init_field_xpos(field
);
1868 if ((field
->overlay
== 0)
1869 || ((field
->overlay
== 1) && (pos
>= row
->length
))) {
1870 /* first check if the field can have more chars...*/
1871 if (check_field_size(field
) == FALSE
)
1872 return E_REQUEST_DENIED
;
1875 >= row
->allocated
) {
1876 new_size
= row
->allocated
+ 16 - (row
->allocated
% 16);
1877 if ((new = (char *) realloc(row
->string
,
1878 (size_t) new_size
)) == NULL
)
1879 return E_SYSTEM_ERROR
;
1880 row
->allocated
= new_size
;
1885 if ((field
->overlay
== 0) && (row
->length
> pos
)) {
1886 bcopy(&row
->string
[pos
], &row
->string
[pos
+ 1],
1887 (size_t) (row
->length
- pos
+ 1));
1890 old_c
= row
->string
[pos
];
1891 row
->string
[pos
] = c
;
1892 if (pos
>= row
->length
) {
1893 /* make sure the string is terminated if we are at the
1894 * end of the string, the terminator would be missing
1895 * if we are are at the end of the field.
1897 row
->string
[pos
+ 1] = '\0';
1900 /* only increment the length if we are inserting characters
1901 * OR if we are at the end of the field in overlay mode.
1903 if ((field
->overlay
== 0)
1904 || ((field
->overlay
== 1) && (pos
>= row
->length
))) {
1908 _formi_calculate_tabs(row
);
1909 row
->expanded
= _formi_tab_expanded_length(row
->string
, 0,
1912 /* wrap the field, if needed */
1913 status
= _formi_wrap_field(field
, row
);
1915 row
= field
->cur_line
;
1916 pos
= field
->row_xpos
;
1919 * check the wrap worked or that we have not exceeded the
1920 * max field size - this can happen if the field is re-wrapped
1921 * and the row count is increased past the set limit.
1923 if ((status
!= E_OK
) || (check_field_size(field
) == FALSE
)) {
1924 if ((field
->overlay
== 0)
1925 || ((field
->overlay
== 1)
1926 && (pos
>= (row
->length
- 1) /*XXXX- append check???*/))) {
1928 * wrap failed for some reason, back out the
1931 bcopy(&row
->string
[pos
+ 1], &row
->string
[pos
],
1932 (size_t) (row
->length
- pos
));
1936 } else if (field
->overlay
== 1) {
1937 /* back out character overlay */
1938 row
->string
[pos
] = old_c
;
1941 _formi_calculate_tabs(row
);
1943 _formi_wrap_field(field
, row
);
1945 * If we are here then either the status is bad or we
1946 * simply ran out of room. If the status is E_OK then
1947 * we ran out of room, let the form driver know this.
1950 status
= E_REQUEST_DENIED
;
1953 field
->buf0_status
= TRUE
;
1955 if ((field
->rows
+ field
->nrows
) == 1) {
1956 status
= _formi_set_cursor_xpos(field
, FALSE
);
1958 field
->cursor_xpos
=
1959 _formi_tab_expanded_length(
1960 row
->string
, 0, field
->row_xpos
- 1);
1963 * Annoying corner case - if we are right in
1964 * the bottom right corner of the field we
1965 * need to scroll the field one line so the
1966 * cursor is positioned correctly in the
1969 if ((field
->cursor_xpos
>= field
->cols
) &&
1970 (field
->cursor_ypos
== (field
->rows
- 1))) {
1971 field
->cursor_ypos
--;
1972 field
->start_line
= field
->start_line
->next
;
1977 assert((field
->cursor_xpos
<= field
->cols
)
1978 && (field
->cursor_ypos
< 400000));
1981 fprintf(dbg
, "add_char exit: xpos=%d, row_pos=%d, start=%d\n",
1982 field
->cursor_xpos
, field
->row_xpos
, field
->start_char
);
1983 fprintf(dbg
, "add_char_exit: length=%d(%d), allocated=%d\n",
1984 row
->expanded
, row
->length
, row
->allocated
);
1985 fprintf(dbg
, "add_char exit: ypos=%d, start_line=%p\n",
1986 field
->cursor_ypos
, field
->start_line
);
1987 fprintf(dbg
,"add_char exit: %s\n", row
->string
);
1988 fprintf(dbg
, "add_char exit: buf0_status=%d\n", field
->buf0_status
);
1989 fprintf(dbg
, "add_char exit: status = %s\n",
1990 (status
== E_OK
)? "OK" : "FAILED");
1996 * Set the position of the cursor on the screen in the row depending on
1997 * where the current position in the string is and the justification
1998 * that is to be applied to the field. Justification is only applied
1999 * to single row, static fields.
2002 _formi_set_cursor_xpos(FIELD
*field
, int noscroll
)
2006 just
= field
->justification
;
2007 pos
= field
->start_char
+ field
->row_xpos
;
2011 "cursor_xpos enter: pos %d, start_char %d, row_xpos %d, xpos %d\n",
2012 pos
, field
->start_char
, field
->row_xpos
, field
->cursor_xpos
);
2016 * make sure we apply the correct justification to non-static
2019 if (((field
->rows
+ field
->nrows
) != 1) ||
2020 ((field
->opts
& O_STATIC
) != O_STATIC
))
2021 just
= JUSTIFY_LEFT
;
2025 field
->cursor_xpos
= field
->cols
- 1
2026 - _formi_tab_expanded_length(
2027 field
->cur_line
->string
, 0,
2028 field
->cur_line
->length
- 1)
2029 + _formi_tab_expanded_length(
2030 field
->cur_line
->string
, 0,
2034 case JUSTIFY_CENTER
:
2035 field
->cursor_xpos
= ((field
->cols
- 1)
2036 - _formi_tab_expanded_length(
2037 field
->cur_line
->string
, 0,
2038 field
->cur_line
->length
- 1) + 1) / 2
2039 + _formi_tab_expanded_length(field
->cur_line
->string
,
2040 0, field
->row_xpos
);
2042 if (field
->cursor_xpos
> (field
->cols
- 1))
2043 field
->cursor_xpos
= (field
->cols
- 1);
2047 field
->cursor_xpos
= _formi_tab_expanded_length(
2048 field
->cur_line
->string
,
2050 field
->row_xpos
+ field
->start_char
);
2051 if ((field
->cursor_xpos
<= (field
->cols
- 1)) &&
2052 ((field
->start_char
+ field
->row_xpos
)
2053 < field
->cur_line
->length
))
2054 field
->cursor_xpos
--;
2056 if (field
->cursor_xpos
> (field
->cols
- 1)) {
2057 if ((field
->opts
& O_STATIC
) == O_STATIC
) {
2058 field
->start_char
= 0;
2061 == (field
->cur_line
->length
- 1)) {
2062 field
->cursor_xpos
= field
->cols
- 1;
2064 field
->cursor_xpos
=
2065 _formi_tab_expanded_length(
2066 field
->cur_line
->string
,
2073 if (noscroll
== FALSE
) {
2080 field
->row_xpos
= pos
2081 - field
->start_char
;
2082 field
->cursor_xpos
=
2083 _formi_tab_expanded_length(
2084 field
->cur_line
->string
,
2087 + field
->start_char
- 1);
2089 field
->cursor_xpos
= (field
->cols
- 1);
2099 "cursor_xpos exit: pos %d, start_char %d, row_xpos %d, xpos %d\n",
2100 pos
, field
->start_char
, field
->row_xpos
, field
->cursor_xpos
);
2106 * Manipulate the text in a field, this takes the given form and performs
2107 * the passed driver command on the current text field. Returns 1 if the
2108 * text field was modified.
2111 _formi_manipulate_field(FORM
*form
, int c
)
2115 unsigned int start
, end
, pos
, status
, old_count
, size
;
2116 unsigned int old_xpos
, old_row_pos
;
2119 _FORMI_FIELD_LINES
*row
, *rs
;
2121 cur
= form
->fields
[form
->cur_field
];
2122 if (cur
->cur_line
->string
== NULL
)
2123 return E_REQUEST_DENIED
;
2126 fprintf(dbg
, "entry: request is REQ_%s\n", reqs
[c
- REQ_MIN_REQUEST
]);
2128 "entry: xpos=%d, row_pos=%d, start_char=%d, length=%d, allocated=%d\n",
2129 cur
->cursor_xpos
, cur
->row_xpos
, cur
->start_char
,
2130 cur
->cur_line
->length
, cur
->cur_line
->allocated
);
2131 fprintf(dbg
, "entry: start_line=%p, ypos=%d\n", cur
->start_line
,
2133 fprintf(dbg
, "entry: string=");
2134 if (cur
->cur_line
->string
== NULL
)
2135 fprintf(dbg
, "(null)\n");
2137 fprintf(dbg
, "\"%s\"\n", cur
->cur_line
->string
);
2140 /* Cannot manipulate a null string! */
2141 if (cur
->cur_line
->string
== NULL
)
2142 return E_REQUEST_DENIED
;
2145 row
= cur
->cur_line
;
2148 case REQ_RIGHT_CHAR
:
2150 * The right_char request performs the same function
2151 * as the next_char request except that the cursor is
2152 * not wrapped if it is at the end of the line, so
2153 * check if the cursor is at the end of the line and
2154 * deny the request otherwise just fall through to
2155 * the next_char request handler.
2157 if (cur
->cursor_xpos
>= cur
->cols
- 1)
2158 return E_REQUEST_DENIED
;
2163 /* for a dynamic field allow an offset of one more
2164 * char so we can insert chars after end of string.
2165 * Static fields cannot do this so deny request if
2166 * cursor is at the end of the field.
2168 if (((cur
->opts
& O_STATIC
) == O_STATIC
) &&
2169 (cur
->row_xpos
== cur
->cols
- 1) &&
2170 ((cur
->rows
+ cur
->nrows
) == 1))
2171 return E_REQUEST_DENIED
;
2173 if (((cur
->rows
+ cur
->nrows
) == 1) &&
2174 (cur
->row_xpos
+ cur
->start_char
+ 1) > row
->length
)
2175 return E_REQUEST_DENIED
;
2177 if ((cur
->rows
+ cur
->nrows
) == 1) {
2179 _formi_set_cursor_xpos(cur
, (c
== REQ_RIGHT_CHAR
));
2181 if (cur
->cursor_xpos
>= (row
->expanded
- 1)) {
2182 if ((row
->next
== NULL
) ||
2183 (c
== REQ_RIGHT_CHAR
))
2184 return E_REQUEST_DENIED
;
2186 cur
->cursor_xpos
= 0;
2188 cur
->cur_line
= cur
->cur_line
->next
;
2189 if (cur
->cursor_ypos
== (cur
->rows
- 1))
2191 cur
->start_line
->next
;
2195 old_xpos
= cur
->cursor_xpos
;
2196 old_row_pos
= cur
->row_xpos
;
2197 if (row
->string
[cur
->row_xpos
] == '\t')
2198 cur
->cursor_xpos
+= tab_size(row
,
2203 if (cur
->cursor_xpos
2205 if ((row
->next
== NULL
) ||
2206 (c
== REQ_RIGHT_CHAR
)) {
2207 cur
->cursor_xpos
= old_xpos
;
2208 cur
->row_xpos
= old_row_pos
;
2209 return E_REQUEST_DENIED
;
2212 cur
->cursor_xpos
= 0;
2214 cur
->cur_line
= cur
->cur_line
->next
;
2215 if (cur
->cursor_ypos
2218 cur
->start_line
->next
;
2229 * The behaviour of left_char is the same as prev_char
2230 * except that the cursor will not wrap if it has
2231 * reached the LHS of the field, so just check this
2232 * and fall through if we are not at the LHS.
2234 if (cur
->cursor_xpos
== 0)
2235 return E_REQUEST_DENIED
;
2239 if ((cur
->rows
+ cur
->nrows
) == 1) {
2240 if (cur
->row_xpos
== 0) {
2241 if (cur
->start_char
> 0)
2244 return E_REQUEST_DENIED
;
2247 _formi_set_cursor_xpos(cur
, FALSE
);
2250 if ((cur
->cursor_xpos
== 0) &&
2251 (cur
->cursor_ypos
== 0) &&
2252 (cur
->start_line
->prev
== NULL
))
2253 return E_REQUEST_DENIED
;
2255 pos
= cur
->row_xpos
;
2256 if (cur
->cursor_xpos
> 0) {
2257 if (row
->string
[pos
] == '\t') {
2258 size
= tab_size(row
, pos
);
2259 if (size
> cur
->cursor_xpos
) {
2260 cur
->cursor_xpos
= 0;
2264 cur
->cursor_xpos
-= size
;
2271 cur
->cur_line
= cur
->cur_line
->prev
;
2272 if (cur
->cursor_ypos
> 0)
2276 cur
->start_line
->prev
;
2277 row
= cur
->cur_line
;
2278 if (row
->expanded
> 0) {
2279 cur
->cursor_xpos
= row
->expanded
- 1;
2281 cur
->cursor_xpos
= 0;
2284 if (row
->length
> 0)
2285 cur
->row_xpos
= row
->length
- 1;
2295 * The down_char request has the same functionality as
2296 * the next_line request excepting that the field is not
2297 * scrolled if the cursor is at the bottom of the field.
2298 * Check to see if the cursor is at the bottom of the field
2299 * and if it is then deny the request otherwise fall
2300 * through to the next_line handler.
2302 if (cur
->cursor_ypos
>= cur
->rows
- 1)
2303 return E_REQUEST_DENIED
;
2308 if ((row
->next
== NULL
) || (cur
->cur_line
->next
== NULL
))
2309 return E_REQUEST_DENIED
;
2311 cur
->cur_line
= cur
->cur_line
->next
;
2312 if ((cur
->cursor_ypos
+ 1) >= cur
->rows
) {
2313 cur
->start_line
= cur
->start_line
->next
;
2316 row
= cur
->cur_line
;
2318 if (row
->length
== 0) {
2320 cur
->cursor_xpos
= 0;
2322 if (cur
->cursor_xpos
> (row
->expanded
- 1))
2323 cur
->cursor_xpos
= row
->expanded
- 1;
2325 cur
->row_xpos
= tab_fit_len(row
, cur
->cursor_xpos
+ 1);
2326 if (cur
->row_xpos
== 0)
2327 cur
->cursor_xpos
= 0;
2330 _formi_tab_expanded_length(
2331 row
->string
, 0, cur
->row_xpos
);
2332 if (cur
->cursor_xpos
> 0)
2339 * The up_char request has the same functionality as
2340 * the prev_line request excepting the field is not
2341 * scrolled, check if the cursor is at the top of the
2342 * field, if it is deny the request otherwise fall
2343 * through to the prev_line handler.
2345 if (cur
->cursor_ypos
== 0)
2346 return E_REQUEST_DENIED
;
2351 if (cur
->cur_line
->prev
== NULL
)
2352 return E_REQUEST_DENIED
;
2354 if (cur
->cursor_ypos
== 0) {
2355 if (cur
->start_line
->prev
== NULL
)
2356 return E_REQUEST_DENIED
;
2357 cur
->start_line
= cur
->start_line
->prev
;
2361 cur
->cur_line
= cur
->cur_line
->prev
;
2362 row
= cur
->cur_line
;
2364 if (row
->length
== 0) {
2366 cur
->cursor_xpos
= 0;
2368 if (cur
->cursor_xpos
> (row
->expanded
- 1))
2369 cur
->cursor_xpos
= row
->expanded
- 1;
2371 cur
->row_xpos
= tab_fit_len(row
, cur
->cursor_xpos
+ 1);
2373 _formi_tab_expanded_length(row
->string
,
2375 if (cur
->cursor_xpos
> 0)
2381 start
= cur
->row_xpos
+ cur
->start_char
;
2384 wb
= find_eow(cur
, start
, FALSE
, &row
);
2389 /* check if we hit the end */
2390 if (str
[start
] == '\0')
2391 return E_REQUEST_DENIED
;
2393 /* otherwise we must have found the start of a word...*/
2394 if ((cur
->rows
+ cur
->nrows
) == 1) {
2395 /* single line field */
2396 size
= _formi_tab_expanded_length(str
,
2397 cur
->start_char
, start
);
2398 if (size
< cur
->cols
) {
2399 cur
->row_xpos
= start
- cur
->start_char
;
2401 cur
->start_char
= start
;
2404 _formi_set_cursor_xpos(cur
, FALSE
);
2406 /* multiline field */
2407 cur
->cur_line
= row
;
2408 adjust_ypos(cur
, row
);
2410 cur
->row_xpos
= start
;
2412 _formi_tab_expanded_length(
2413 row
->string
, 0, cur
->row_xpos
) - 1;
2418 start
= cur
->start_char
+ cur
->row_xpos
;
2419 if (cur
->start_char
> 0)
2422 if ((start
== 0) && (row
->prev
== NULL
))
2423 return E_REQUEST_DENIED
;
2427 if (row
->length
> 0)
2428 start
= row
->length
- 1;
2435 start
= find_sow(start
, &row
);
2437 if ((cur
->rows
+ cur
->nrows
) == 1) {
2438 /* single line field */
2439 size
= _formi_tab_expanded_length(str
,
2440 cur
->start_char
, start
);
2442 if (start
> cur
->start_char
) {
2443 cur
->row_xpos
= start
- cur
->start_char
;
2445 cur
->start_char
= start
;
2448 _formi_set_cursor_xpos(cur
, FALSE
);
2450 /* multiline field */
2451 cur
->cur_line
= row
;
2452 adjust_ypos(cur
, row
);
2453 cur
->row_xpos
= start
;
2455 _formi_tab_expanded_length(
2463 cur
->start_char
= 0;
2464 while (cur
->start_line
->prev
!= NULL
)
2465 cur
->start_line
= cur
->start_line
->prev
;
2466 cur
->cur_line
= cur
->start_line
;
2468 _formi_init_field_xpos(cur
);
2469 cur
->cursor_ypos
= 0;
2474 _formi_init_field_xpos(cur
);
2475 cur
->start_char
= 0;
2479 while (cur
->cur_line
->next
!= NULL
)
2480 cur
->cur_line
= cur
->cur_line
->next
;
2482 if (cur
->row_count
> cur
->rows
) {
2483 cur
->start_line
= cur
->cur_line
;
2484 pos
= cur
->rows
- 1;
2486 cur
->start_line
= cur
->start_line
->prev
;
2489 cur
->cursor_ypos
= cur
->rows
- 1;
2491 cur
->cursor_ypos
= cur
->row_count
- 1;
2494 /* we fall through here deliberately, we are on the
2495 * correct row, now we need to get to the end of the
2501 row
= cur
->cur_line
;
2503 if ((cur
->rows
+ cur
->nrows
) == 1) {
2504 if (row
->expanded
> cur
->cols
- 1) {
2505 if ((cur
->opts
& O_STATIC
) != O_STATIC
) {
2506 cur
->start_char
= tab_fit_window(
2509 cur
->row_xpos
= row
->length
2512 cur
->start_char
= 0;
2513 cur
->row_xpos
= cur
->cols
- 1;
2516 cur
->row_xpos
= row
->length
+ 1;
2517 cur
->start_char
= 0;
2519 _formi_set_cursor_xpos(cur
, FALSE
);
2521 cur
->row_xpos
= row
->length
- 1;
2522 cur
->cursor_xpos
= row
->expanded
- 1;
2523 if (row
->next
== NULL
) {
2531 start
= cur
->start_char
+ cur
->row_xpos
;
2532 if ((status
= split_line(cur
, TRUE
, start
, &row
)) != E_OK
)
2534 cur
->cur_line
->hard_ret
= TRUE
;
2535 cur
->cursor_xpos
= 0;
2540 if ((status
= _formi_add_char(cur
, cur
->start_char
2547 if ((status
= split_line(cur
, TRUE
, 0, &row
)) != E_OK
)
2549 cur
->cur_line
->hard_ret
= TRUE
;
2553 row
= cur
->cur_line
;
2554 start
= cur
->start_char
+ cur
->row_xpos
;
2555 end
= row
->length
- 1;
2556 if ((start
>= row
->length
) && (row
->next
== NULL
))
2557 return E_REQUEST_DENIED
;
2559 if ((start
== row
->length
- 1) || (row
->length
== 0)) {
2560 if ((cur
->rows
+ cur
->nrows
) > 1) {
2562 * Firstly, check if the current line has
2563 * a hard return. In this case we just
2564 * want to "delete" the hard return and
2565 * re-wrap the field. The hard return
2566 * does not occupy a character space in
2567 * the buffer but we must make it appear
2568 * like it does for a deletion.
2570 if (row
->hard_ret
== TRUE
) {
2571 row
->hard_ret
= FALSE
;
2572 if (_formi_join_line(cur
, &row
,
2575 row
->hard_ret
= TRUE
;
2583 * If we have more than one row, join the
2584 * next row to make things easier unless
2585 * we are at the end of the string, in
2586 * that case the join would fail but we
2587 * really want to delete the last char
2590 if (row
->next
!= NULL
) {
2591 if (_formi_join_line(cur
, &row
,
2594 return E_REQUEST_DENIED
;
2600 saved
= row
->string
[start
];
2601 bcopy(&row
->string
[start
+ 1], &row
->string
[start
],
2602 (size_t) (end
- start
+ 1));
2603 row
->string
[end
] = '\0';
2605 if (row
->length
> 0)
2606 row
->expanded
= _formi_tab_expanded_length(
2607 row
->string
, 0, row
->length
- 1);
2612 * recalculate tabs for a single line field, multiline
2613 * fields will do this when the field is wrapped.
2615 if ((cur
->rows
+ cur
->nrows
) == 1)
2616 _formi_calculate_tabs(row
);
2618 * if we are at the end of the string then back the
2619 * cursor pos up one to stick on the end of the line
2621 if (start
== row
->length
) {
2622 if (row
->length
> 1) {
2623 if ((cur
->rows
+ cur
->nrows
) == 1) {
2624 pos
= cur
->row_xpos
+ cur
->start_char
;
2628 cur
->start_char
+ cur
->row_xpos
,
2630 cur
->row_xpos
= pos
- cur
->start_char
2632 _formi_set_cursor_xpos(cur
, FALSE
);
2634 if (cur
->row_xpos
== 0) {
2635 if (row
->next
!= NULL
) {
2636 if (_formi_join_line(
2640 return E_REQUEST_DENIED
;
2643 if (cur
->row_count
> 1)
2649 cur
->row_xpos
= start
- 1;
2651 _formi_tab_expanded_length(
2653 0, cur
->row_xpos
- 1);
2654 if ((cur
->cursor_xpos
> 0)
2655 && (start
!= (row
->expanded
- 1)))
2663 _formi_init_field_xpos(cur
);
2667 if ((cur
->rows
+ cur
->nrows
) > 1) {
2668 if (_formi_wrap_field(cur
, row
) != E_OK
) {
2669 bcopy(&row
->string
[start
],
2670 &row
->string
[start
+ 1],
2671 (size_t) (end
- start
));
2673 row
->string
[start
] = saved
;
2674 _formi_wrap_field(cur
, row
);
2675 return E_REQUEST_DENIED
;
2681 if ((cur
->cursor_xpos
== 0) && (cur
->start_char
== 0)
2682 && (cur
->start_line
->prev
== NULL
)
2683 && (cur
->cursor_ypos
== 0))
2684 return E_REQUEST_DENIED
;
2686 row
= cur
->cur_line
;
2687 start
= cur
->row_xpos
+ cur
->start_char
;
2688 end
= row
->length
- 1;
2691 if ((cur
->start_char
+ cur
->row_xpos
) == 0) {
2692 if (row
->prev
== NULL
)
2693 return E_REQUEST_DENIED
;
2696 * If we are a multiline field then check if
2697 * the line above has a hard return. If it does
2698 * then just "eat" the hard return and re-wrap
2701 if (row
->prev
->hard_ret
== TRUE
) {
2702 row
->prev
->hard_ret
= FALSE
;
2703 if (_formi_join_line(cur
, &row
,
2704 JOIN_PREV
) != E_OK
) {
2705 row
->prev
->hard_ret
= TRUE
;
2711 start
= row
->prev
->length
;
2713 * Join this line to the previous
2716 if (_formi_join_line(cur
, &row
,
2717 JOIN_PREV_NW
) != E_OK
) {
2720 end
= row
->length
- 1;
2724 if (eat_char
== TRUE
) {
2726 * eat a char from the buffer. Normally we do
2727 * this unless we have deleted a "hard return"
2728 * in which case we just want to join the lines
2729 * without losing a char.
2731 saved
= row
->string
[start
- 1];
2732 bcopy(&row
->string
[start
], &row
->string
[start
- 1],
2733 (size_t) (end
- start
+ 1));
2735 row
->string
[row
->length
] = '\0';
2736 row
->expanded
= _formi_tab_expanded_length(
2737 row
->string
, 0, row
->length
- 1);
2740 if ((cur
->rows
+ cur
->nrows
) == 1) {
2741 _formi_calculate_tabs(row
);
2742 pos
= cur
->row_xpos
+ cur
->start_char
;
2747 cur
->start_char
+ cur
->row_xpos
,
2749 cur
->row_xpos
= pos
- cur
->start_char
;
2750 _formi_set_cursor_xpos(cur
, FALSE
);
2752 if (eat_char
== TRUE
) {
2754 if (cur
->row_xpos
> 0)
2756 _formi_tab_expanded_length(
2760 cur
->cursor_xpos
= 0;
2763 if ((_formi_wrap_field(cur
, row
) != E_OK
)) {
2764 bcopy(&row
->string
[start
- 1],
2765 &row
->string
[start
],
2766 (size_t) (end
- start
));
2768 row
->string
[start
- 1] = saved
;
2769 row
->string
[row
->length
] = '\0';
2770 _formi_wrap_field(cur
, row
);
2771 return E_REQUEST_DENIED
;
2777 if (((cur
->rows
+ cur
->nrows
) == 1) ||
2778 (cur
->row_count
== 1)) {
2779 /* single line case */
2781 row
->expanded
= row
->length
= 0;
2783 _formi_init_field_xpos(cur
);
2784 cur
->cursor_ypos
= 0;
2786 /* multiline field */
2787 old_count
= cur
->row_count
;
2789 if (cur
->row_count
== 0)
2792 if (old_count
== 1) {
2793 row
->expanded
= row
->length
= 0;
2794 cur
->cursor_xpos
= 0;
2796 cur
->cursor_ypos
= 0;
2798 add_to_free(cur
, row
);
2800 if (row
->next
== NULL
) {
2801 if (cur
->cursor_ypos
== 0) {
2802 if (cur
->start_line
->prev
!= NULL
) {
2804 cur
->start_line
->prev
;
2811 if (old_count
> 1) {
2812 if (cur
->cursor_xpos
> row
->expanded
) {
2813 cur
->cursor_xpos
= row
->expanded
- 1;
2814 cur
->row_xpos
= row
->length
- 1;
2817 cur
->start_line
= cur
->lines
;
2818 rs
= cur
->start_line
;
2819 cur
->cursor_ypos
= 0;
2821 if (cur
->cursor_ypos
< cur
->rows
)
2825 cur
->start_line
->next
;
2833 start
= cur
->start_char
+ cur
->row_xpos
;
2836 wb
= find_eow(cur
, start
, TRUE
, &row
);
2843 * If not at the start of a word then find the start,
2844 * we cannot blindly call find_sow because this will
2845 * skip back a word if we are already at the start of
2849 && !(isblank(str
[start
- 1]) && !isblank(str
[start
])))
2850 start
= find_sow(start
, &row
);
2852 /* XXXX hmmmm what if start and end on diff rows? XXXX */
2853 bcopy(&str
[end
], &str
[start
],
2854 (size_t) (row
->length
- end
+ 1));
2858 if ((cur
->rows
+ cur
->nrows
) > 1) {
2859 row
= cur
->start_line
+ cur
->cursor_ypos
;
2860 if (row
->next
!= NULL
) {
2862 * if not on the last row we need to
2863 * join on the next row so the line
2864 * will be re-wrapped.
2866 _formi_join_line(cur
, &row
, JOIN_NEXT_NW
);
2868 _formi_wrap_field(cur
, row
);
2869 cur
->row_xpos
= start
;
2870 cur
->cursor_xpos
= _formi_tab_expanded_length(
2871 row
->string
, 0, cur
->row_xpos
);
2872 if (cur
->cursor_xpos
> 0)
2875 _formi_calculate_tabs(row
);
2876 cur
->row_xpos
= start
- cur
->start_char
;
2877 if (cur
->row_xpos
> 0)
2879 _formi_set_cursor_xpos(cur
, FALSE
);
2884 row
->string
[cur
->row_xpos
+ 1] = '\0';
2885 row
->length
= cur
->row_xpos
+ 1;
2886 row
->expanded
= cur
->cursor_xpos
+ 1;
2890 row
= cur
->cur_line
->next
;
2891 while (row
!= NULL
) {
2893 add_to_free(cur
, row
);
2900 row
= cur
->lines
->next
;
2901 cur
->cur_line
= cur
->lines
;
2902 cur
->start_line
= cur
->lines
;
2904 while (row
!= NULL
) {
2906 add_to_free(cur
, row
);
2910 cur
->lines
->string
[0] = '\0';
2911 cur
->lines
->length
= 0;
2912 cur
->lines
->expanded
= 0;
2914 cur
->cursor_ypos
= 0;
2916 _formi_init_field_xpos(cur
);
2917 cur
->start_char
= 0;
2929 _formi_scroll_fwd(cur
, 1);
2933 _formi_scroll_back(cur
, 1);
2937 _formi_scroll_fwd(cur
, cur
->rows
);
2941 _formi_scroll_back(cur
, cur
->rows
);
2944 case REQ_SCR_FHPAGE
:
2945 _formi_scroll_fwd(cur
, cur
->rows
/ 2);
2948 case REQ_SCR_BHPAGE
:
2949 _formi_scroll_back(cur
, cur
->rows
/ 2);
2953 _formi_hscroll_fwd(cur
, row
, 1);
2957 _formi_hscroll_back(cur
, row
, 1);
2960 case REQ_SCR_HFLINE
:
2961 _formi_hscroll_fwd(cur
, row
, cur
->cols
);
2964 case REQ_SCR_HBLINE
:
2965 _formi_hscroll_back(cur
, row
, cur
->cols
);
2968 case REQ_SCR_HFHALF
:
2969 _formi_hscroll_fwd(cur
, row
, cur
->cols
/ 2);
2972 case REQ_SCR_HBHALF
:
2973 _formi_hscroll_back(cur
, row
, cur
->cols
/ 2);
2982 "exit: cursor_xpos=%d, row_xpos=%d, start_char=%d, length=%d, allocated=%d\n",
2983 cur
->cursor_xpos
, cur
->row_xpos
, cur
->start_char
,
2984 cur
->cur_line
->length
, cur
->cur_line
->allocated
);
2985 fprintf(dbg
, "exit: start_line=%p, ypos=%d\n", cur
->start_line
,
2987 fprintf(dbg
, "exit: string=\"%s\"\n", cur
->cur_line
->string
);
2988 assert ((cur
->cursor_xpos
< INT_MAX
) && (cur
->row_xpos
< INT_MAX
)
2989 && (cur
->cursor_xpos
>= cur
->row_xpos
));
2995 * Validate the give character by passing it to any type character
2996 * checking routines, if they exist.
2999 _formi_validate_char(FIELD
*field
, char c
)
3003 if (field
->type
== NULL
)
3006 ret_val
= E_INVALID_FIELD
;
3007 _formi_do_char_validation(field
, field
->type
, c
, &ret_val
);
3014 * Perform the validation of the character, invoke all field_type validation
3015 * routines. If the field is ok then update ret_val to E_OK otherwise
3016 * ret_val is not changed.
3019 _formi_do_char_validation(FIELD
*field
, FIELDTYPE
*type
, char c
, int *ret_val
)
3021 if ((type
->flags
& _TYPE_IS_LINKED
) == _TYPE_IS_LINKED
) {
3022 _formi_do_char_validation(field
, type
->link
->next
, c
, ret_val
);
3023 _formi_do_char_validation(field
, type
->link
->prev
, c
, ret_val
);
3025 if (type
->char_check
== NULL
)
3028 if (type
->char_check((int)(unsigned char) c
,
3029 field
->args
) == TRUE
)
3036 * Validate the current field. If the field validation returns success then
3037 * return E_OK otherwise return E_INVALID_FIELD.
3041 _formi_validate_field(FORM
*form
)
3047 if ((form
== NULL
) || (form
->fields
== NULL
) ||
3048 (form
->fields
[0] == NULL
))
3049 return E_INVALID_FIELD
;
3051 cur
= form
->fields
[form
->cur_field
];
3054 * Sync the buffer if it has been modified so the field
3055 * validation routines can use it and because this is
3056 * the correct behaviour according to AT&T implementation.
3058 if ((cur
->buf0_status
== TRUE
)
3059 && ((ret_val
= _formi_sync_buffer(cur
)) != E_OK
))
3063 * If buffer is untouched then the string pointer may be
3064 * NULL, see if this is ok or not.
3066 if (cur
->buffers
[0].string
== NULL
) {
3067 if ((cur
->opts
& O_NULLOK
) == O_NULLOK
)
3070 return E_INVALID_FIELD
;
3073 count
= _formi_skip_blanks(cur
->buffers
[0].string
, 0);
3075 /* check if we have a null field, depending on the nullok flag
3076 * this may be acceptable or not....
3078 if (cur
->buffers
[0].string
[count
] == '\0') {
3079 if ((cur
->opts
& O_NULLOK
) == O_NULLOK
)
3082 return E_INVALID_FIELD
;
3085 /* check if an unmodified field is ok */
3086 if (cur
->buf0_status
== 0) {
3087 if ((cur
->opts
& O_PASSOK
) == O_PASSOK
)
3090 return E_INVALID_FIELD
;
3093 /* if there is no type then just accept the field */
3094 if (cur
->type
== NULL
)
3097 ret_val
= E_INVALID_FIELD
;
3098 _formi_do_validation(cur
, cur
->type
, &ret_val
);
3104 * Perform the validation of the field, invoke all field_type validation
3105 * routines. If the field is ok then update ret_val to E_OK otherwise
3106 * ret_val is not changed.
3109 _formi_do_validation(FIELD
*field
, FIELDTYPE
*type
, int *ret_val
)
3111 if ((type
->flags
& _TYPE_IS_LINKED
) == _TYPE_IS_LINKED
) {
3112 _formi_do_validation(field
, type
->link
->next
, ret_val
);
3113 _formi_do_validation(field
, type
->link
->prev
, ret_val
);
3115 if (type
->field_check
== NULL
)
3118 if (type
->field_check(field
, field_buffer(field
, 0))
3126 * Select the next/previous choice for the field, the driver command
3127 * selecting the direction will be passed in c. Return 1 if a choice
3128 * selection succeeded, 0 otherwise.
3131 _formi_field_choice(FORM
*form
, int c
)
3136 if ((form
== NULL
) || (form
->fields
== NULL
) ||
3137 (form
->fields
[0] == NULL
) ||
3138 (form
->fields
[form
->cur_field
]->type
== NULL
))
3141 field
= form
->fields
[form
->cur_field
];
3145 case REQ_NEXT_CHOICE
:
3146 if (type
->next_choice
== NULL
)
3149 return type
->next_choice(field
,
3150 field_buffer(field
, 0));
3152 case REQ_PREV_CHOICE
:
3153 if (type
->prev_choice
== NULL
)
3156 return type
->prev_choice(field
,
3157 field_buffer(field
, 0));
3159 default: /* should never happen! */
3165 * Update the fields if they have changed. The parameter old has the
3166 * previous current field as the current field may have been updated by
3167 * the driver. Return 1 if the form page needs updating.
3171 _formi_update_field(FORM
*form
, int old_field
)
3175 cur
= form
->cur_field
;
3177 if (old_field
!= cur
) {
3178 if (!((cur
>= form
->page_starts
[form
->page
].first
) &&
3179 (cur
<= form
->page_starts
[form
->page
].last
))) {
3180 /* not on same page any more */
3181 for (i
= 0; i
< form
->max_page
; i
++) {
3182 if ((form
->page_starts
[i
].in_use
== 1) &&
3183 (form
->page_starts
[i
].first
<= cur
) &&
3184 (form
->page_starts
[i
].last
>= cur
)) {
3192 _formi_redraw_field(form
, old_field
);
3193 _formi_redraw_field(form
, form
->cur_field
);
3198 * Compare function for the field sorting
3202 field_sort_compare(const void *one
, const void *two
)
3207 /* LINTED const castaway; we don't modify these! */
3208 a
= (const FIELD
*) *((const FIELD
**) one
);
3209 b
= (const FIELD
*) *((const FIELD
**) two
);
3218 * First check the page, we want the fields sorted by page.
3221 if (a
->page
!= b
->page
)
3222 return ((a
->page
> b
->page
)? 1 : -1);
3224 tl
= _formi_top_left(a
->parent
, a
->index
, b
->index
);
3227 * sort fields left to right, top to bottom so the top left is
3228 * the lesser value....
3230 return ((tl
== a
->index
)? -1 : 1);
3234 * Sort the fields in a form ready for driver traversal.
3237 _formi_sort_fields(FORM
*form
)
3242 CIRCLEQ_INIT(&form
->sorted_fields
);
3244 if ((sort_area
= (FIELD
**) malloc(sizeof(FIELD
*) * form
->field_count
))
3248 bcopy(form
->fields
, sort_area
,
3249 (size_t) (sizeof(FIELD
*) * form
->field_count
));
3250 qsort(sort_area
, (size_t) form
->field_count
, sizeof(FIELD
*),
3251 field_sort_compare
);
3253 for (i
= 0; i
< form
->field_count
; i
++)
3254 CIRCLEQ_INSERT_TAIL(&form
->sorted_fields
, sort_area
[i
], glue
);
3260 * Set the neighbours for all the fields in the given form.
3263 _formi_stitch_fields(FORM
*form
)
3265 int above_row
, below_row
, end_above
, end_below
, cur_row
, real_end
;
3266 FIELD
*cur
, *above
, *below
;
3269 * check if the sorted fields circle queue is empty, just
3272 if (CIRCLEQ_EMPTY(&form
->sorted_fields
))
3275 /* initially nothing is above..... */
3280 /* set up the first field as the current... */
3281 cur
= CIRCLEQ_FIRST(&form
->sorted_fields
);
3282 cur_row
= cur
->form_row
;
3284 /* find the first field on the next row if any */
3285 below
= CIRCLEQ_NEXT(cur
, glue
);
3289 while (below
!= (void *)&form
->sorted_fields
) {
3290 if (below
->form_row
!= cur_row
) {
3291 below_row
= below
->form_row
;
3296 below
= CIRCLEQ_NEXT(below
, glue
);
3299 /* walk the sorted fields, setting the neighbour pointers */
3300 while (cur
!= (void *) &form
->sorted_fields
) {
3301 if (cur
== CIRCLEQ_FIRST(&form
->sorted_fields
))
3304 cur
->left
= CIRCLEQ_PREV(cur
, glue
);
3306 if (cur
== CIRCLEQ_LAST(&form
->sorted_fields
))
3309 cur
->right
= CIRCLEQ_NEXT(cur
, glue
);
3311 if (end_above
== TRUE
)
3315 above
= CIRCLEQ_NEXT(above
, glue
);
3316 if (above_row
!= above
->form_row
) {
3318 above_row
= above
->form_row
;
3322 if (end_below
== TRUE
)
3326 below
= CIRCLEQ_NEXT(below
, glue
);
3327 if (below
== (void *) &form
->sorted_fields
) {
3330 } else if (below_row
!= below
->form_row
) {
3332 below_row
= below
->form_row
;
3336 cur
= CIRCLEQ_NEXT(cur
, glue
);
3337 if ((cur
!= (void *) &form
->sorted_fields
)
3338 && (cur_row
!= cur
->form_row
)) {
3339 cur_row
= cur
->form_row
;
3340 if (end_above
== FALSE
) {
3341 for (; above
!= CIRCLEQ_FIRST(&form
->sorted_fields
);
3342 above
= CIRCLEQ_NEXT(above
, glue
)) {
3343 if (above
->form_row
!= above_row
) {
3344 above_row
= above
->form_row
;
3348 } else if (above
== NULL
) {
3349 above
= CIRCLEQ_FIRST(&form
->sorted_fields
);
3351 above_row
= above
->form_row
;
3355 if (end_below
== FALSE
) {
3356 while (below_row
== below
->form_row
) {
3357 below
= CIRCLEQ_NEXT(below
,
3360 (void *)&form
->sorted_fields
) {
3367 if (below
!= (void *)&form
->sorted_fields
)
3368 below_row
= below
->form_row
;
3369 } else if (real_end
== FALSE
)
3377 * Calculate the length of the displayed line allowing for any tab
3378 * characters that need to be expanded. We assume that the tab stops
3379 * are 8 characters apart. The parameters start and end are the
3380 * character positions in the string str we want to get the length of,
3381 * the function returns the number of characters from the start
3382 * position to the end position that should be displayed after any
3383 * intervening tabs have been expanded.
3386 _formi_tab_expanded_length(char *str
, unsigned int start
, unsigned int end
)
3388 int len
, start_len
, i
;
3390 /* if we have a null string then there is no length */
3398 * preceding tabs affect the length tabs in the span, so
3399 * we need to calculate the length including the stuff before
3400 * start and then subtract off the unwanted bit.
3402 for (i
= 0; i
<= end
; i
++) {
3403 if (i
== start
) /* stash preamble length for later */
3410 len
= len
- (len
% 8) + 8;
3418 "tab_expanded: start=%d, end=%d, expanded=%d (diff=%d)\n",
3419 start
, end
, (len
- start_len
), (end
- start
));
3423 return (len
- start_len
);
3427 * Calculate the tab stops on a given line in the field and set up
3428 * the tabs list with the results. We do this by scanning the line for tab
3429 * characters and if one is found, noting the position and the number of
3430 * characters to get to the next tab stop. This information is kept to
3431 * make manipulating the field (scrolling and so on) easier to handle.
3434 _formi_calculate_tabs(_FORMI_FIELD_LINES
*row
)
3436 _formi_tab_t
*ts
= row
->tabs
, *old_ts
= NULL
, **tsp
;
3440 * If the line already has tabs then invalidate them by
3441 * walking the list and killing the in_use flag.
3443 for (; ts
!= NULL
; ts
= ts
->fwd
)
3448 * Now look for tabs in the row and record the info...
3451 for (i
= 0, j
= 0; i
< row
->length
; i
++, j
++) {
3452 if (row
->string
[i
] == '\t') {
3454 if ((*tsp
= (_formi_tab_t
*)
3455 malloc(sizeof(_formi_tab_t
))) == NULL
)
3457 (*tsp
)->back
= old_ts
;
3461 (*tsp
)->in_use
= TRUE
;
3463 (*tsp
)->size
= 8 - (j
% 8);
3464 j
+= (*tsp
)->size
- 1;
3472 * Return the size of the tab padding for a tab character at the given
3473 * position. Return 1 if there is not a tab char entry matching the
3477 tab_size(_FORMI_FIELD_LINES
*row
, unsigned int i
)
3482 while ((ts
!= NULL
) && (ts
->pos
!= i
))
3492 * Find the character offset that corresponds to longest tab expanded
3493 * string that will fit into the given window. Walk the string backwards
3494 * evaluating the sizes of any tabs that are in the string. Note that
3495 * using this function on a multi-line window will produce undefined
3496 * results - it is really only required for a single row field.
3499 tab_fit_window(FIELD
*field
, unsigned int pos
, unsigned int window
)
3504 /* first find the last tab */
3505 ts
= field
->lines
->tabs
;
3508 * unless there are no tabs - just return the window size,
3509 * if there is enough room, otherwise 0.
3512 if (field
->lines
->length
< window
)
3515 return field
->lines
->length
- window
+ 1;
3518 while ((ts
->fwd
!= NULL
) && (ts
->fwd
->in_use
== TRUE
))
3522 * now walk backwards finding the first tab that is to the
3523 * left of our starting pos.
3525 while ((ts
!= NULL
) && (ts
->in_use
== TRUE
) && (ts
->pos
> pos
))
3529 for (i
= pos
; i
>= 0; i
--) {
3530 if (field
->lines
->string
[i
] == '\t') {
3531 assert((ts
!= NULL
) && (ts
->in_use
== TRUE
));
3533 if ((scroll_amt
+ ts
->size
) > window
) {
3536 scroll_amt
+= ts
->size
;
3540 assert(ts
->pos
== i
);
3543 if (scroll_amt
> window
)
3552 * Return the position of the last character that will fit into the
3553 * given width after tabs have been expanded for a given row of a given
3557 tab_fit_len(_FORMI_FIELD_LINES
*row
, unsigned int width
)
3559 unsigned int pos
, len
, row_pos
;
3570 while ((len
< width
) && (pos
< row
->length
)) {
3571 if (row
->string
[pos
] == '\t') {
3572 assert((ts
!= NULL
) && (ts
->in_use
== TRUE
));
3573 if (ts
->pos
== row_pos
) {
3574 if ((len
+ ts
->size
) > width
)
3580 assert(ts
->pos
== row_pos
);
3593 * Sync the field line structures with the contents of buffer 0 for that
3594 * field. We do this by walking all the line structures and concatenating
3595 * all the strings into one single string in buffer 0.
3598 _formi_sync_buffer(FIELD
*field
)
3600 _FORMI_FIELD_LINES
*line
;
3604 if (field
->lines
== NULL
)
3605 return E_BAD_ARGUMENT
;
3607 if (field
->lines
->string
== NULL
)
3608 return E_BAD_ARGUMENT
;
3611 * init nstr up front, just in case there are no line contents,
3612 * this could happen if the field just contains hard returns.
3614 if ((nstr
= malloc(sizeof(char))) == NULL
)
3615 return E_SYSTEM_ERROR
;
3618 line
= field
->lines
;
3619 length
= 1; /* allow for terminating null */
3621 while (line
!= NULL
) {
3622 if (line
->length
!= 0) {
3623 if ((tmp
= realloc(nstr
,
3624 (size_t) (length
+ line
->length
)))
3628 return (E_SYSTEM_ERROR
);
3632 strcat(nstr
, line
->string
);
3633 length
+= line
->length
;
3639 if (field
->buffers
[0].string
!= NULL
)
3640 free(field
->buffers
[0].string
);
3641 field
->buffers
[0].allocated
= length
;
3642 field
->buffers
[0].length
= length
- 1;
3643 field
->buffers
[0].string
= nstr
;