1 /* $NetBSD: internals.c,v 1.37 2013/11/26 01:17:00 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.37 2013/11/26 01:17:00 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
->alines
;
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
->alines
= line
->next
;
182 field
->alines
->prev
= NULL
;
184 if (field
->cur_line
== saved
)
185 field
->cur_line
= field
->alines
;
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
->alines
->length
< field
->max
);
356 return (field
->row_count
<= field
->max
);
359 if ((field
->rows
+ field
->nrows
) == 1) {
360 return (field
->alines
->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
= TAILQ_NEXT(cur
, glue
);
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
== TAILQ_LAST(&form
->sorted_fields
,
425 return E_REQUEST_DENIED
;
426 cur
= TAILQ_NEXT(cur
, glue
);
429 if ((form
->wrap
== FALSE
) &&
430 ((i
+ 1) >= form
->field_count
))
431 return E_REQUEST_DENIED
;
433 if (i
>= form
->field_count
)
437 if (use_sorted
== TRUE
) {
438 if ((form
->wrap
== FALSE
) &&
439 (cur
== TAILQ_FIRST(&form
->sorted_fields
)))
440 return E_REQUEST_DENIED
;
441 cur
= TAILQ_PREV(cur
, _formi_sort_head
, glue
);
444 if ((form
->wrap
== FALSE
) && (i
<= 0))
445 return E_REQUEST_DENIED
;
448 i
= form
->field_count
- 1;
452 if ((form
->fields
[i
]->opts
& (O_VISIBLE
| O_ACTIVE
))
453 == (O_VISIBLE
| O_ACTIVE
)) {
458 while (i
!= form
->cur_field
);
460 return E_REQUEST_DENIED
;
464 * Destroy the list of line structs passed by freeing all allocated
468 destroy_row_list(_FORMI_FIELD_LINES
*start
)
470 _FORMI_FIELD_LINES
*temp
, *row
;
471 _formi_tab_t
*tt
, *tp
;
474 while (row
!= NULL
) {
475 if (row
->tabs
!= NULL
) {
476 /* free up the tab linked list... */
485 if (row
->string
!= NULL
)
495 * Word wrap the contents of the field's buffer 0 if this is allowed.
496 * If the wrap is successful, that is, the row count nor the buffer
497 * size is exceeded then the function will return E_OK, otherwise it
498 * will return E_REQUEST_DENIED.
501 _formi_wrap_field(FIELD
*field
, _FORMI_FIELD_LINES
*loc
)
504 unsigned int pos
, saved_xpos
, saved_ypos
, saved_cur_xpos
;
505 unsigned int saved_row_count
;
506 _FORMI_FIELD_LINES
*saved_row
, *row
, *row_backup
, *saved_cur_line
;
507 _FORMI_FIELD_LINES
*saved_start_line
, *temp
;
509 if ((field
->opts
& O_STATIC
) == O_STATIC
) {
510 if ((field
->rows
+ field
->nrows
) == 1) {
511 return E_OK
; /* cannot wrap a single line */
515 /* if we are limited to one line then don't try to wrap */
516 if ((field
->drows
+ field
->nrows
) == 1) {
521 * hueristic - if a dynamic field has more than one line
522 * on the screen then the field grows rows, otherwise
523 * it grows columns, effectively a single line field.
524 * This is documented AT&T behaviour.
526 if (field
->rows
> 1) {
535 /* if we are not at the top of the field then back up one
536 * row because we may be able to merge the current row into
539 if (row
->prev
!= NULL
)
543 saved_xpos
= field
->row_xpos
;
544 saved_cur_xpos
= field
->cursor_xpos
;
545 saved_ypos
= field
->cursor_ypos
;
546 saved_row_count
= field
->row_count
;
549 * Save a copy of the lines affected, just in case things
552 if ((row_backup
= copy_row(row
)) == NULL
)
553 return E_SYSTEM_ERROR
;
558 saved_cur_line
= temp
;
559 saved_start_line
= temp
;
561 while (row
!= NULL
) {
562 if ((temp
->next
= copy_row(row
)) == NULL
) {
563 /* a row copy failed... free up allocations */
564 destroy_row_list(row_backup
);
565 return E_SYSTEM_ERROR
;
568 temp
->next
->prev
= temp
;
571 if (row
== field
->start_line
)
572 saved_start_line
= temp
;
573 if (row
== field
->cur_line
)
574 saved_cur_line
= temp
;
580 while (row
!= NULL
) {
581 pos
= row
->length
- 1;
582 if (row
->expanded
< width
) {
583 /* line may be too short, try joining some lines */
584 if ((row
->hard_ret
== TRUE
) && (row
->next
!= NULL
)) {
586 * Skip the line if it has a hard return
587 * and it is not the last, we cannot join
594 if (row
->next
== NULL
) {
596 * If there are no more lines and this line
597 * is too short then our job is over.
602 if (_formi_join_line(field
, &row
,
603 JOIN_NEXT_NW
) == E_OK
) {
607 } else if (row
->expanded
> width
) {
608 /* line is too long, split it */
611 * split on first whitespace before current word
612 * if the line has tabs we need to work out where
613 * the field border lies when the tabs are expanded.
615 if (row
->tabs
== NULL
) {
617 if (pos
>= row
->expanded
)
618 pos
= row
->expanded
- 1;
620 pos
= tab_fit_len(row
, field
->cols
);
623 if ((!isblank((unsigned char)row
->string
[pos
])) &&
624 ((field
->opts
& O_WRAP
) == O_WRAP
)) {
625 if (!isblank((unsigned char)row
->string
[pos
- 1]))
626 pos
= find_sow((unsigned int) pos
,
629 * If we cannot split the line then return
630 * NO_ROOM so the driver can tell that it
631 * should not autoskip (if that is enabled)
634 || (!isblank((unsigned char)row
->string
[pos
- 1]))) {
635 wrap_err
= E_NO_ROOM
;
636 goto restore_and_exit
;
640 /* if we are at the end of the string and it has
641 * a trailing blank, don't wrap the blank.
643 if ((row
->next
== NULL
) && (pos
== row
->length
- 1) &&
644 (isblank((unsigned char)row
->string
[pos
])) &&
645 row
->expanded
<= field
->cols
)
649 * otherwise, if we are still sitting on a
650 * blank but not at the end of the line
651 * move forward one char so the blank
652 * is on the line boundary.
654 if ((isblank((unsigned char)row
->string
[pos
])) &&
655 (pos
!= row
->length
- 1))
658 if (split_line(field
, FALSE
, pos
, &row
) != E_OK
) {
659 wrap_err
= E_REQUEST_DENIED
;
660 goto restore_and_exit
;
663 /* line is exactly the right length, do next one */
667 /* Check if we have not run out of room */
668 if ((((field
->opts
& O_STATIC
) == O_STATIC
) &&
669 field
->row_count
> (field
->rows
+ field
->nrows
)) ||
670 ((field
->max
!= 0) && (field
->row_count
> field
->max
))) {
672 wrap_err
= E_REQUEST_DENIED
;
675 if (saved_row
->prev
== NULL
) {
676 field
->alines
= row_backup
;
678 saved_row
->prev
->next
= row_backup
;
679 row_backup
->prev
= saved_row
->prev
;
682 field
->row_xpos
= saved_xpos
;
683 field
->cursor_xpos
= saved_cur_xpos
;
684 field
->cursor_ypos
= saved_ypos
;
685 field
->row_count
= saved_row_count
;
686 field
->start_line
= saved_start_line
;
687 field
->cur_line
= saved_cur_line
;
689 destroy_row_list(saved_row
);
693 destroy_row_list(row_backup
);
698 * Join the two lines that surround the location pos, the type
699 * variable indicates the direction of the join, JOIN_NEXT will join
700 * the next line to the current line, JOIN_PREV will join the current
701 * line to the previous line, the new lines will be wrapped unless the
702 * _NW versions of the directions are used. Returns E_OK if the join
703 * was successful or E_REQUEST_DENIED if the join cannot happen.
706 _formi_join_line(FIELD
*field
, _FORMI_FIELD_LINES
**rowp
, int direction
)
709 struct _formi_field_lines
*saved
;
711 _FORMI_FIELD_LINES
*row
= *rowp
;
715 if (_formi_create_dbg_file() == E_OK
) {
719 if (dbg_ok
== TRUE
) {
720 fprintf(dbg
, "join_line: working on row %p, row_count = %d\n",
721 row
, field
->row_count
);
725 if ((direction
== JOIN_NEXT
) || (direction
== JOIN_NEXT_NW
)) {
727 * See if there is another line following, or if the
728 * line contains a hard return then we don't join
731 if ((row
->next
== NULL
) || (row
->hard_ret
== TRUE
)) {
732 return E_REQUEST_DENIED
;
736 if (dbg_ok
== TRUE
) {
738 "join_line: join_next before length = %d, expanded = %d",
739 row
->length
, row
->expanded
);
741 " :: next row length = %d, expanded = %d\n",
742 row
->length
, row
->expanded
);
746 if (row
->allocated
< (row
->length
+ row
->next
->length
+ 1)) {
747 if ((newp
= realloc(row
->string
, (size_t)(row
->length
+
750 return E_REQUEST_DENIED
;
752 row
->allocated
= row
->length
+ row
->next
->length
+ 1;
755 strcat(row
->string
, row
->next
->string
);
756 old_len
= row
->length
;
757 row
->length
+= row
->next
->length
;
760 _formi_tab_expanded_length(row
->string
, 0,
765 _formi_calculate_tabs(row
);
766 row
->hard_ret
= row
->next
->hard_ret
;
768 /* adjust current line if it is on the row being eaten */
769 if (field
->cur_line
== row
->next
) {
770 field
->cur_line
= row
;
771 field
->row_xpos
+= old_len
;
773 _formi_tab_expanded_length(row
->string
, 0,
775 if (field
->cursor_xpos
> 0)
776 field
->cursor_xpos
--;
778 if (field
->cursor_ypos
> 0)
779 field
->cursor_ypos
--;
781 if (field
->start_line
->prev
!= NULL
)
783 field
->start_line
->prev
;
787 /* remove joined line record from the row list */
788 add_to_free(field
, row
->next
);
791 if (dbg_ok
== TRUE
) {
793 "join_line: exit length = %d, expanded = %d\n",
794 row
->length
, row
->expanded
);
798 if (row
->prev
== NULL
) {
799 return E_REQUEST_DENIED
;
805 * Don't try to join if the line above has a hard
808 if (saved
->hard_ret
== TRUE
) {
809 return E_REQUEST_DENIED
;
813 if (dbg_ok
== TRUE
) {
815 "join_line: join_prev before length = %d, expanded = %d",
816 row
->length
, row
->expanded
);
818 " :: prev row length = %d, expanded = %d\n",
819 saved
->length
, saved
->expanded
);
823 if (saved
->allocated
< (row
->length
+ saved
->length
+ 1)) {
824 if ((newp
= realloc(saved
->string
,
825 (size_t) (row
->length
+
828 return E_REQUEST_DENIED
;
829 saved
->string
= newp
;
830 saved
->allocated
= row
->length
+ saved
->length
+ 1;
833 strcat(saved
->string
, row
->string
);
834 old_len
= saved
->length
;
835 saved
->length
+= row
->length
;
836 if (saved
->length
> 0)
838 _formi_tab_expanded_length(saved
->string
, 0,
843 saved
->hard_ret
= row
->hard_ret
;
845 /* adjust current line if it was on the row being eaten */
846 if (field
->cur_line
== row
) {
847 field
->cur_line
= saved
;
848 field
->row_xpos
+= old_len
;
850 _formi_tab_expanded_length(saved
->string
, 0,
852 if (field
->cursor_xpos
> 0)
853 field
->cursor_xpos
--;
856 add_to_free(field
, row
);
859 if (dbg_ok
== TRUE
) {
861 "join_line: exit length = %d, expanded = %d\n",
862 saved
->length
, saved
->expanded
);
870 * Work out where the line lies in the field in relation to
871 * the cursor_ypos. First count the rows from the start of
872 * the field until we hit the row we just worked on.
874 saved
= field
->start_line
;
876 while (saved
->next
!= NULL
) {
883 /* now check if we need to adjust cursor_ypos */
884 if (field
->cursor_ypos
> count
) {
885 field
->cursor_ypos
--;
891 /* wrap the field if required, if this fails undo the change */
892 if ((direction
== JOIN_NEXT
) || (direction
== JOIN_PREV
)) {
893 if (_formi_wrap_field(field
, row
) != E_OK
) {
894 return E_REQUEST_DENIED
;
902 * Split the line at the given position, if possible. If hard_split is
903 * TRUE then split the line regardless of the position, otherwise don't
904 * split at the beginning of a line.
907 split_line(FIELD
*field
, bool hard_split
, unsigned pos
,
908 _FORMI_FIELD_LINES
**rowp
)
910 struct _formi_field_lines
*new_line
;
912 _FORMI_FIELD_LINES
*row
= *rowp
;
914 short dbg_ok
= FALSE
;
917 /* if asked to split right where the line already starts then
918 * just return - nothing to do unless we are appending a line
921 if ((pos
== 0) && (hard_split
== FALSE
))
925 if (_formi_create_dbg_file() == E_OK
) {
926 fprintf(dbg
, "split_line: splitting line at %d\n", pos
);
931 /* Need an extra line struct, check free list first */
932 if (field
->free
!= NULL
) {
933 new_line
= field
->free
;
934 field
->free
= new_line
->next
;
935 if (field
->free
!= NULL
)
936 field
->free
->prev
= NULL
;
938 if ((new_line
= (struct _formi_field_lines
*)
939 malloc(sizeof(struct _formi_field_lines
))) == NULL
)
940 return E_SYSTEM_ERROR
;
941 new_line
->prev
= NULL
;
942 new_line
->next
= NULL
;
943 new_line
->allocated
= 0;
944 new_line
->length
= 0;
945 new_line
->expanded
= 0;
946 new_line
->string
= NULL
;
947 new_line
->hard_ret
= FALSE
;
948 new_line
->tabs
= NULL
;
952 if (dbg_ok
== TRUE
) {
954 "split_line: enter: length = %d, expanded = %d\n",
955 row
->length
, row
->expanded
);
959 assert((row
->length
< INT_MAX
) && (row
->expanded
< INT_MAX
));
962 /* add new line to the row list */
963 new_line
->next
= row
->next
;
964 new_line
->prev
= row
;
965 row
->next
= new_line
;
966 if (new_line
->next
!= NULL
)
967 new_line
->next
->prev
= new_line
;
969 new_line
->length
= row
->length
- pos
;
970 if (new_line
->length
>= new_line
->allocated
) {
971 if ((newp
= realloc(new_line
->string
,
972 (size_t) new_line
->length
+ 1)) == NULL
)
973 return E_SYSTEM_ERROR
;
974 new_line
->string
= newp
;
975 new_line
->allocated
= new_line
->length
+ 1;
978 strcpy(new_line
->string
, &row
->string
[pos
]);
981 row
->string
[pos
] = '\0';
983 if (row
->length
!= 0)
984 row
->expanded
= _formi_tab_expanded_length(row
->string
, 0,
988 _formi_calculate_tabs(row
);
990 if (new_line
->length
!= 0)
992 _formi_tab_expanded_length(new_line
->string
, 0,
993 new_line
->length
- 1);
995 new_line
->expanded
= 0;
997 _formi_calculate_tabs(new_line
);
1000 * If the given row was the current line then adjust the
1001 * current line pointer if necessary
1003 if ((field
->cur_line
== row
) && (field
->row_xpos
>= pos
)) {
1004 field
->cur_line
= new_line
;
1005 field
->row_xpos
-= pos
;
1006 field
->cursor_xpos
=
1007 _formi_tab_expanded_length(new_line
->string
, 0,
1009 if (field
->cursor_xpos
> 0)
1010 field
->cursor_xpos
--;
1012 field
->cursor_ypos
++;
1013 if (field
->cursor_ypos
>= field
->rows
) {
1014 if (field
->start_line
->next
!= NULL
) {
1015 field
->start_line
= field
->start_line
->next
;
1016 field
->cursor_ypos
= field
->rows
- 1;
1019 assert(field
->start_line
->next
== NULL
);
1024 * If the line split had a hard return then replace the
1025 * current line's hard return with a soft return and carry
1026 * the hard return onto the line after.
1028 if (row
->hard_ret
== TRUE
) {
1029 new_line
->hard_ret
= TRUE
;
1030 row
->hard_ret
= FALSE
;
1034 * except where we are doing a hard split then the current
1035 * row must have a hard return on it too...
1037 if (hard_split
== TRUE
) {
1038 row
->hard_ret
= TRUE
;
1041 assert(((row
->expanded
< INT_MAX
) &&
1042 (new_line
->expanded
< INT_MAX
) &&
1043 (row
->length
< INT_MAX
) &&
1044 (new_line
->length
< INT_MAX
)));
1047 if (dbg_ok
== TRUE
) {
1048 fprintf(dbg
, "split_line: exit: ");
1049 fprintf(dbg
, "row.length = %d, row.expanded = %d, ",
1050 row
->length
, row
->expanded
);
1052 "next_line.length = %d, next_line.expanded = %d, ",
1053 new_line
->length
, new_line
->expanded
);
1054 fprintf(dbg
, "row_count = %d\n", field
->row_count
+ 1);
1065 * skip the blanks in the given string, start at the index start and
1066 * continue forward until either the end of the string or a non-blank
1067 * character is found. Return the index of either the end of the string or
1068 * the first non-blank character.
1071 _formi_skip_blanks(char *string
, unsigned int start
)
1077 while ((string
[i
] != '\0') && isblank((unsigned char)string
[i
]))
1084 * Skip the blanks in the string associated with the given row, pass back
1085 * the row and the offset at which the first non-blank is found. If no
1086 * non-blank character is found then return the index to the last
1087 * character on the last line.
1091 field_skip_blanks(unsigned int start
, _FORMI_FIELD_LINES
**rowp
)
1094 _FORMI_FIELD_LINES
*row
, *last
= NULL
;
1100 i
= _formi_skip_blanks(&row
->string
[i
], i
);
1101 if (!isblank((unsigned char)row
->string
[i
])) {
1105 * don't reset if last line otherwise we will
1106 * not be at the end of the string.
1113 while (row
!= NULL
);
1116 * If we hit the end of the row list then point at the last row
1117 * otherwise we return the row we found the blank on.
1128 * Return the index of the top left most field of the two given fields.
1131 _formi_top_left(FORM
*form
, int a
, int b
)
1133 /* lower row numbers always win here.... */
1134 if (form
->fields
[a
]->form_row
< form
->fields
[b
]->form_row
)
1137 if (form
->fields
[a
]->form_row
> form
->fields
[b
]->form_row
)
1140 /* rows must be equal, check columns */
1141 if (form
->fields
[a
]->form_col
< form
->fields
[b
]->form_col
)
1144 if (form
->fields
[a
]->form_col
> form
->fields
[b
]->form_col
)
1147 /* if we get here fields must be in exactly the same place, punt */
1152 * Return the index to the field that is the bottom-right-most of the
1156 _formi_bottom_right(FORM
*form
, int a
, int b
)
1158 /* check the rows first, biggest row wins */
1159 if (form
->fields
[a
]->form_row
> form
->fields
[b
]->form_row
)
1161 if (form
->fields
[a
]->form_row
< form
->fields
[b
]->form_row
)
1164 /* rows must be equal, check cols, biggest wins */
1165 if (form
->fields
[a
]->form_col
> form
->fields
[b
]->form_col
)
1167 if (form
->fields
[a
]->form_col
< form
->fields
[b
]->form_col
)
1170 /* fields in the same place, punt */
1175 * Find the end of the current word in the string str, starting at
1176 * offset - the end includes any trailing whitespace. If the end of
1177 * the string is found before a new word then just return the offset
1178 * to the end of the string. If do_join is TRUE then lines will be
1179 * joined (without wrapping) until either the end of the field or the
1180 * end of a word is found (whichever comes first).
1183 find_eow(FIELD
*cur
, unsigned int offset
, bool do_join
,
1184 _FORMI_FIELD_LINES
**rowp
)
1187 _FORMI_FIELD_LINES
*row
;
1193 /* first skip any non-whitespace */
1194 while ((row
->string
[start
] != '\0')
1195 && !isblank((unsigned char)row
->string
[start
]))
1198 /* see if we hit the end of the string */
1199 if (row
->string
[start
] == '\0') {
1200 if (do_join
== TRUE
) {
1201 if (row
->next
== NULL
)
1204 if (_formi_join_line(cur
, &row
, JOIN_NEXT_NW
)
1206 return E_REQUEST_DENIED
;
1209 if (row
->next
== NULL
) {
1216 } while (row
->length
== 0);
1219 } while (!isblank((unsigned char)row
->string
[start
]));
1222 /* otherwise skip the whitespace.... */
1223 while ((row
->string
[start
] != '\0')
1224 && isblank((unsigned char)row
->string
[start
]))
1227 if (row
->string
[start
] == '\0') {
1228 if (do_join
== TRUE
) {
1229 if (row
->next
== NULL
)
1232 if (_formi_join_line(cur
, &row
, JOIN_NEXT_NW
)
1234 return E_REQUEST_DENIED
;
1237 if (row
->next
== NULL
) {
1244 } while (row
->length
== 0);
1247 } while (isblank((unsigned char)row
->string
[start
]));
1254 * Find the beginning of the current word in the string str, starting
1258 find_sow(unsigned int offset
, _FORMI_FIELD_LINES
**rowp
)
1262 _FORMI_FIELD_LINES
*row
;
1270 if (isblank((unsigned char)str
[start
]) ||
1271 isblank((unsigned char)str
[start
- 1])) {
1272 if (isblank((unsigned char)str
[start
- 1]))
1274 /* skip the whitespace.... */
1275 while ((start
>= 0) &&
1276 isblank((unsigned char)str
[start
]))
1281 /* see if we hit the start of the string */
1284 if (row
->prev
== NULL
) {
1291 if (row
->length
> 0)
1292 start
= row
->length
- 1;
1296 } while (row
->length
== 0);
1298 } while (isblank((unsigned char)row
->string
[start
]));
1300 /* see if we hit the start of the string */
1306 /* now skip any non-whitespace */
1308 while ((start
>= 0) && !isblank((unsigned char)str
[start
]))
1314 if (row
->prev
== NULL
) {
1321 if (row
->length
> 0)
1322 start
= row
->length
- 1;
1326 } while (row
->length
== 0);
1328 } while (!isblank((unsigned char)str
[start
]));
1331 start
++; /* last loop has us pointing at a space, adjust */
1332 if (start
>= row
->length
) {
1333 if (row
->next
!= NULL
) {
1337 start
= row
->length
- 1;
1350 * Scroll the field forward the given number of lines.
1353 _formi_scroll_fwd(FIELD
*field
, unsigned int amt
)
1356 _FORMI_FIELD_LINES
*end_row
;
1358 end_row
= field
->start_line
;
1359 /* walk the line structs forward to find the bottom of the field */
1360 count
= field
->rows
- 1;
1361 while ((count
> 0) && (end_row
->next
!= NULL
))
1364 end_row
= end_row
->next
;
1367 /* check if there are lines to scroll */
1368 if ((count
> 0) && (end_row
->next
== NULL
))
1372 * ok, lines to scroll - do this by walking both the start_line
1373 * and the end_row at the same time for amt lines, we stop when
1374 * either we have done the number of lines or end_row hits the
1375 * last line in the field.
1378 while ((count
> 0) && (end_row
->next
!= NULL
)) {
1380 field
->start_line
= field
->start_line
->next
;
1381 end_row
= end_row
->next
;
1386 * Scroll the field backward the given number of lines.
1389 _formi_scroll_back(FIELD
*field
, unsigned int amt
)
1393 /* check for lines above */
1394 if (field
->start_line
->prev
== NULL
)
1398 * Backward scroll is easy, follow row struct chain backward until
1399 * the number of lines done or we reach the top of the field.
1402 while ((count
> 0) && (field
->start_line
->prev
!= NULL
)) {
1404 field
->start_line
= field
->start_line
->prev
;
1409 * Scroll the field forward the given number of characters.
1412 _formi_hscroll_fwd(FIELD
*field
, _FORMI_FIELD_LINES
*row
, int unsigned amt
)
1414 unsigned int end
, scroll_amt
, expanded
;
1418 if ((row
->tabs
== NULL
) || (row
->tabs
->in_use
== FALSE
)) {
1419 /* if the line has no tabs things are easy... */
1420 end
= field
->start_char
+ field
->cols
+ amt
- 1;
1422 if (end
> row
->length
) {
1424 scroll_amt
= end
- field
->start_char
- field
->cols
+ 1;
1428 * If there are tabs we need to add on the scroll amount,
1429 * find the last char position that will fit into
1430 * the field and finally fix up the start_char. This
1431 * is a lot of work but handling the case where there
1432 * are not enough chars to scroll by amt is difficult.
1434 end
= field
->start_char
+ field
->row_xpos
+ amt
;
1435 if (end
>= row
->length
)
1436 end
= row
->length
- 1;
1438 expanded
= _formi_tab_expanded_length(
1440 field
->start_char
+ amt
,
1441 field
->start_char
+ field
->row_xpos
+ amt
);
1443 /* skip tabs to the lhs of our starting point */
1444 while ((ts
!= NULL
) && (ts
->in_use
== TRUE
)
1448 while ((expanded
<= field
->cols
)
1449 && (end
< row
->length
)) {
1450 if (row
->string
[end
] == '\t') {
1452 && (ts
->in_use
== TRUE
));
1453 if (ts
->pos
== end
) {
1454 if ((expanded
+ ts
->size
)
1457 expanded
+= ts
->size
;
1461 assert(ts
->pos
== end
);
1468 scroll_amt
= tab_fit_window(field
, end
, field
->cols
);
1469 if (scroll_amt
< field
->start_char
)
1472 scroll_amt
-= field
->start_char
;
1474 scroll_amt
= min(scroll_amt
, amt
);
1477 field
->start_char
+= scroll_amt
;
1478 field
->cursor_xpos
=
1479 _formi_tab_expanded_length(row
->string
,
1482 + field
->start_char
) - 1;
1487 * Scroll the field backward the given number of characters.
1490 _formi_hscroll_back(FIELD
*field
, _FORMI_FIELD_LINES
*row
, unsigned int amt
)
1492 field
->start_char
-= min(field
->start_char
, amt
);
1493 field
->cursor_xpos
=
1494 _formi_tab_expanded_length(row
->string
, field
->start_char
,
1496 + field
->start_char
) - 1;
1497 if (field
->cursor_xpos
>= field
->cols
) {
1498 field
->row_xpos
= 0;
1499 field
->cursor_xpos
= 0;
1504 * Find the different pages in the form fields and assign the form
1505 * page_starts array with the information to find them.
1508 _formi_find_pages(FORM
*form
)
1510 int i
, cur_page
= 0;
1512 if ((form
->page_starts
= (_FORMI_PAGE_START
*)
1513 malloc((form
->max_page
+ 1) * sizeof(_FORMI_PAGE_START
))) == NULL
)
1514 return E_SYSTEM_ERROR
;
1516 /* initialise the page starts array */
1517 memset(form
->page_starts
, 0,
1518 (form
->max_page
+ 1) * sizeof(_FORMI_PAGE_START
));
1520 for (i
=0; i
< form
->field_count
; i
++) {
1521 if (form
->fields
[i
]->page_break
== 1)
1523 if (form
->page_starts
[cur_page
].in_use
== 0) {
1524 form
->page_starts
[cur_page
].in_use
= 1;
1525 form
->page_starts
[cur_page
].first
= i
;
1526 form
->page_starts
[cur_page
].last
= i
;
1527 form
->page_starts
[cur_page
].top_left
= i
;
1528 form
->page_starts
[cur_page
].bottom_right
= i
;
1530 form
->page_starts
[cur_page
].last
= i
;
1531 form
->page_starts
[cur_page
].top_left
=
1532 _formi_top_left(form
,
1533 form
->page_starts
[cur_page
].top_left
,
1535 form
->page_starts
[cur_page
].bottom_right
=
1536 _formi_bottom_right(form
,
1537 form
->page_starts
[cur_page
].bottom_right
,
1546 * Completely redraw the field of the given form.
1549 _formi_redraw_field(FORM
*form
, int field
)
1551 unsigned int pre
, post
, flen
, slen
, i
, j
, start
, line
;
1552 unsigned int tab
, cpos
, len
;
1555 _FORMI_FIELD_LINES
*row
;
1560 cur
= form
->fields
[field
];
1566 for (row
= cur
->start_line
; ((row
!= NULL
) && (line
< cur
->rows
));
1567 row
= row
->next
, line
++) {
1568 wmove(form
->scrwin
, (int) (cur
->form_row
+ line
),
1569 (int) cur
->form_col
);
1570 if ((cur
->rows
+ cur
->nrows
) == 1) {
1571 if ((cur
->cols
+ cur
->start_char
) >= row
->length
)
1574 len
= cur
->cols
+ cur
->start_char
;
1575 if (row
->string
!= NULL
)
1576 slen
= _formi_tab_expanded_length(
1577 row
->string
, cur
->start_char
, len
);
1581 if (slen
> cur
->cols
)
1583 slen
+= cur
->start_char
;
1585 slen
= row
->expanded
;
1587 if ((cur
->opts
& O_STATIC
) == O_STATIC
) {
1588 switch (cur
->justification
) {
1597 case JUSTIFY_CENTER
:
1603 post
= pre
= pre
/ 2;
1604 /* get padding right if
1605 centring is not even */
1606 if ((post
+ pre
+ slen
) < flen
)
1611 case NO_JUSTIFICATION
:
1625 /* dynamic fields are not justified */
1635 /* but they do scroll.... */
1637 if (pre
> cur
->start_char
- start
)
1638 pre
= pre
- cur
->start_char
+ start
;
1642 if (slen
> cur
->start_char
) {
1643 slen
-= cur
->start_char
;
1657 if (form
->cur_field
== field
)
1658 wattrset(form
->scrwin
, cur
->fore
);
1660 wattrset(form
->scrwin
, cur
->back
);
1662 str
= &row
->string
[cur
->start_char
];
1665 if (_formi_create_dbg_file() == E_OK
) {
1667 "redraw_field: start=%d, pre=%d, slen=%d, flen=%d, post=%d, start_char=%d\n",
1668 start
, pre
, slen
, flen
, post
, cur
->start_char
);
1670 if (row
->expanded
!= 0) {
1671 strncpy(buffer
, str
, flen
);
1673 strcpy(buffer
, "(empty)");
1676 strcpy(buffer
, "(null)");
1678 buffer
[flen
] = '\0';
1679 fprintf(dbg
, "redraw_field: %s\n", buffer
);
1683 for (i
= start
+ cur
->start_char
; i
< pre
; i
++)
1684 waddch(form
->scrwin
, cur
->pad
);
1687 fprintf(dbg
, "redraw_field: will add %d chars\n",
1690 for (i
= 0, cpos
= cur
->start_char
; i
< min(slen
, flen
);
1694 tab
= 0; /* just to shut gcc up */
1696 fprintf(dbg
, "adding char str[%d]=%c\n",
1697 cpos
+ cur
->start_char
, c
);
1699 if (((cur
->opts
& O_PUBLIC
) != O_PUBLIC
)) {
1701 tab
= add_tab(form
, row
, cpos
,
1704 waddch(form
->scrwin
, cur
->pad
);
1705 } else if ((cur
->opts
& O_VISIBLE
) == O_VISIBLE
) {
1707 tab
= add_tab(form
, row
, cpos
, ' ');
1709 waddch(form
->scrwin
, c
);
1712 tab
= add_tab(form
, row
, cpos
, ' ');
1714 waddch(form
->scrwin
, ' ');
1718 * If we have had a tab then skip forward
1719 * the requisite number of chars to keep
1726 for (i
= 0; i
< post
; i
++)
1727 waddch(form
->scrwin
, cur
->pad
);
1730 for (i
= line
; i
< cur
->rows
; i
++) {
1731 wmove(form
->scrwin
, (int) (cur
->form_row
+ i
),
1732 (int) cur
->form_col
);
1734 if (form
->cur_field
== field
)
1735 wattrset(form
->scrwin
, cur
->fore
);
1737 wattrset(form
->scrwin
, cur
->back
);
1739 for (j
= 0; j
< cur
->cols
; j
++) {
1740 waddch(form
->scrwin
, cur
->pad
);
1744 wattrset(form
->scrwin
, cur
->back
);
1749 * Add the correct number of the given character to simulate a tab
1753 add_tab(FORM
*form
, _FORMI_FIELD_LINES
*row
, unsigned int i
, char c
)
1756 _formi_tab_t
*ts
= row
->tabs
;
1758 while ((ts
!= NULL
) && (ts
->pos
!= i
))
1763 for (j
= 0; j
< ts
->size
; j
++)
1764 waddch(form
->scrwin
, c
);
1771 * Display the fields attached to the form that are on the current page
1776 _formi_draw_page(FORM
*form
)
1780 if (form
->page_starts
[form
->page
].in_use
== 0)
1781 return E_BAD_ARGUMENT
;
1783 wclear(form
->scrwin
);
1785 for (i
= form
->page_starts
[form
->page
].first
;
1786 i
<= form
->page_starts
[form
->page
].last
; i
++)
1787 _formi_redraw_field(form
, i
);
1793 * Add the character c at the position pos in buffer 0 of the given field
1796 _formi_add_char(FIELD
*field
, unsigned int pos
, char c
)
1799 unsigned int new_size
;
1801 _FORMI_FIELD_LINES
*row
, *temp
, *next_temp
;
1803 row
= field
->cur_line
;
1806 * If buffer has not had a string before, set it to a blank
1807 * string. Everything should flow from there....
1809 if (row
->string
== NULL
) {
1810 if ((row
->string
= (char *) malloc((size_t)INITIAL_LINE_ALLOC
))
1812 return E_SYSTEM_ERROR
;
1813 row
->string
[0] = '\0';
1814 row
->allocated
= INITIAL_LINE_ALLOC
;
1819 if (_formi_validate_char(field
, c
) != E_OK
) {
1821 fprintf(dbg
, "add_char: char %c failed char validation\n", c
);
1823 return E_INVALID_FIELD
;
1826 if ((c
== '\t') && (field
->cols
<= 8)) {
1828 fprintf(dbg
, "add_char: field too small for a tab\n");
1834 fprintf(dbg
, "add_char: pos=%d, char=%c\n", pos
, c
);
1835 fprintf(dbg
, "add_char enter: xpos=%d, row_pos=%d, start=%d\n",
1836 field
->cursor_xpos
, field
->row_xpos
, field
->start_char
);
1837 fprintf(dbg
, "add_char enter: length=%d(%d), allocated=%d\n",
1838 row
->expanded
, row
->length
, row
->allocated
);
1839 fprintf(dbg
, "add_char enter: %s\n", row
->string
);
1840 fprintf(dbg
, "add_char enter: buf0_status=%d\n", field
->buf0_status
);
1842 if (((field
->opts
& O_BLANK
) == O_BLANK
) &&
1843 (field
->buf0_status
== FALSE
) &&
1844 ((field
->row_xpos
+ field
->start_char
) == 0)) {
1845 row
= field
->alines
;
1846 if (row
->next
!= NULL
) {
1847 /* shift all but one line structs to free list */
1850 next_temp
= temp
->next
;
1851 add_to_free(field
, temp
);
1853 } while (temp
!= NULL
);
1857 row
->string
[0] = '\0';
1859 field
->start_char
= 0;
1860 field
->start_line
= row
;
1861 field
->cur_line
= row
;
1862 field
->row_count
= 1;
1863 field
->row_xpos
= 0;
1864 field
->cursor_ypos
= 0;
1867 _formi_init_field_xpos(field
);
1871 if ((field
->overlay
== 0)
1872 || ((field
->overlay
== 1) && (pos
>= row
->length
))) {
1873 /* first check if the field can have more chars...*/
1874 if (check_field_size(field
) == FALSE
)
1875 return E_REQUEST_DENIED
;
1878 >= row
->allocated
) {
1879 new_size
= row
->allocated
+ 16 - (row
->allocated
% 16);
1880 if ((new = (char *) realloc(row
->string
,
1881 (size_t) new_size
)) == NULL
)
1882 return E_SYSTEM_ERROR
;
1883 row
->allocated
= new_size
;
1888 if ((field
->overlay
== 0) && (row
->length
> pos
)) {
1889 bcopy(&row
->string
[pos
], &row
->string
[pos
+ 1],
1890 (size_t) (row
->length
- pos
+ 1));
1893 old_c
= row
->string
[pos
];
1894 row
->string
[pos
] = c
;
1895 if (pos
>= row
->length
) {
1896 /* make sure the string is terminated if we are at the
1897 * end of the string, the terminator would be missing
1898 * if we are are at the end of the field.
1900 row
->string
[pos
+ 1] = '\0';
1903 /* only increment the length if we are inserting characters
1904 * OR if we are at the end of the field in overlay mode.
1906 if ((field
->overlay
== 0)
1907 || ((field
->overlay
== 1) && (pos
>= row
->length
))) {
1911 _formi_calculate_tabs(row
);
1912 row
->expanded
= _formi_tab_expanded_length(row
->string
, 0,
1915 /* wrap the field, if needed */
1916 status
= _formi_wrap_field(field
, row
);
1918 row
= field
->cur_line
;
1919 pos
= field
->row_xpos
;
1922 * check the wrap worked or that we have not exceeded the
1923 * max field size - this can happen if the field is re-wrapped
1924 * and the row count is increased past the set limit.
1926 if ((status
!= E_OK
) || (check_field_size(field
) == FALSE
)) {
1927 if ((field
->overlay
== 0)
1928 || ((field
->overlay
== 1)
1929 && (pos
>= (row
->length
- 1) /*XXXX- append check???*/))) {
1931 * wrap failed for some reason, back out the
1934 bcopy(&row
->string
[pos
+ 1], &row
->string
[pos
],
1935 (size_t) (row
->length
- pos
));
1939 } else if (field
->overlay
== 1) {
1940 /* back out character overlay */
1941 row
->string
[pos
] = old_c
;
1944 _formi_calculate_tabs(row
);
1946 _formi_wrap_field(field
, row
);
1948 * If we are here then either the status is bad or we
1949 * simply ran out of room. If the status is E_OK then
1950 * we ran out of room, let the form driver know this.
1953 status
= E_REQUEST_DENIED
;
1956 field
->buf0_status
= TRUE
;
1958 if ((field
->rows
+ field
->nrows
) == 1) {
1959 status
= _formi_set_cursor_xpos(field
, FALSE
);
1961 field
->cursor_xpos
=
1962 _formi_tab_expanded_length(
1963 row
->string
, 0, field
->row_xpos
- 1);
1966 * Annoying corner case - if we are right in
1967 * the bottom right corner of the field we
1968 * need to scroll the field one line so the
1969 * cursor is positioned correctly in the
1972 if ((field
->cursor_xpos
>= field
->cols
) &&
1973 (field
->cursor_ypos
== (field
->rows
- 1))) {
1974 field
->cursor_ypos
--;
1975 field
->start_line
= field
->start_line
->next
;
1980 assert((field
->cursor_xpos
<= field
->cols
)
1981 && (field
->cursor_ypos
< 400000));
1984 fprintf(dbg
, "add_char exit: xpos=%d, row_pos=%d, start=%d\n",
1985 field
->cursor_xpos
, field
->row_xpos
, field
->start_char
);
1986 fprintf(dbg
, "add_char_exit: length=%d(%d), allocated=%d\n",
1987 row
->expanded
, row
->length
, row
->allocated
);
1988 fprintf(dbg
, "add_char exit: ypos=%d, start_line=%p\n",
1989 field
->cursor_ypos
, field
->start_line
);
1990 fprintf(dbg
,"add_char exit: %s\n", row
->string
);
1991 fprintf(dbg
, "add_char exit: buf0_status=%d\n", field
->buf0_status
);
1992 fprintf(dbg
, "add_char exit: status = %s\n",
1993 (status
== E_OK
)? "OK" : "FAILED");
1999 * Set the position of the cursor on the screen in the row depending on
2000 * where the current position in the string is and the justification
2001 * that is to be applied to the field. Justification is only applied
2002 * to single row, static fields.
2005 _formi_set_cursor_xpos(FIELD
*field
, int noscroll
)
2009 just
= field
->justification
;
2010 pos
= field
->start_char
+ field
->row_xpos
;
2014 "cursor_xpos enter: pos %d, start_char %d, row_xpos %d, xpos %d\n",
2015 pos
, field
->start_char
, field
->row_xpos
, field
->cursor_xpos
);
2019 * make sure we apply the correct justification to non-static
2022 if (((field
->rows
+ field
->nrows
) != 1) ||
2023 ((field
->opts
& O_STATIC
) != O_STATIC
))
2024 just
= JUSTIFY_LEFT
;
2028 field
->cursor_xpos
= field
->cols
- 1
2029 - _formi_tab_expanded_length(
2030 field
->cur_line
->string
, 0,
2031 field
->cur_line
->length
- 1)
2032 + _formi_tab_expanded_length(
2033 field
->cur_line
->string
, 0,
2037 case JUSTIFY_CENTER
:
2038 field
->cursor_xpos
= ((field
->cols
- 1)
2039 - _formi_tab_expanded_length(
2040 field
->cur_line
->string
, 0,
2041 field
->cur_line
->length
- 1) + 1) / 2
2042 + _formi_tab_expanded_length(field
->cur_line
->string
,
2043 0, field
->row_xpos
);
2045 if (field
->cursor_xpos
> (field
->cols
- 1))
2046 field
->cursor_xpos
= (field
->cols
- 1);
2050 field
->cursor_xpos
= _formi_tab_expanded_length(
2051 field
->cur_line
->string
,
2053 field
->row_xpos
+ field
->start_char
);
2054 if ((field
->cursor_xpos
<= (field
->cols
- 1)) &&
2055 ((field
->start_char
+ field
->row_xpos
)
2056 < field
->cur_line
->length
))
2057 field
->cursor_xpos
--;
2059 if (field
->cursor_xpos
> (field
->cols
- 1)) {
2060 if ((field
->opts
& O_STATIC
) == O_STATIC
) {
2061 field
->start_char
= 0;
2064 == (field
->cur_line
->length
- 1)) {
2065 field
->cursor_xpos
= field
->cols
- 1;
2067 field
->cursor_xpos
=
2068 _formi_tab_expanded_length(
2069 field
->cur_line
->string
,
2076 if (noscroll
== FALSE
) {
2083 field
->row_xpos
= pos
2084 - field
->start_char
;
2085 field
->cursor_xpos
=
2086 _formi_tab_expanded_length(
2087 field
->cur_line
->string
,
2090 + field
->start_char
- 1);
2092 field
->cursor_xpos
= (field
->cols
- 1);
2102 "cursor_xpos exit: pos %d, start_char %d, row_xpos %d, xpos %d\n",
2103 pos
, field
->start_char
, field
->row_xpos
, field
->cursor_xpos
);
2109 * Manipulate the text in a field, this takes the given form and performs
2110 * the passed driver command on the current text field. Returns 1 if the
2111 * text field was modified.
2114 _formi_manipulate_field(FORM
*form
, int c
)
2118 unsigned int start
, end
, pos
, status
, old_count
, size
;
2119 unsigned int old_xpos
, old_row_pos
;
2122 _FORMI_FIELD_LINES
*row
, *rs
;
2124 cur
= form
->fields
[form
->cur_field
];
2125 if (cur
->cur_line
->string
== NULL
)
2126 return E_REQUEST_DENIED
;
2129 fprintf(dbg
, "entry: request is REQ_%s\n", reqs
[c
- REQ_MIN_REQUEST
]);
2131 "entry: xpos=%d, row_pos=%d, start_char=%d, length=%d, allocated=%d\n",
2132 cur
->cursor_xpos
, cur
->row_xpos
, cur
->start_char
,
2133 cur
->cur_line
->length
, cur
->cur_line
->allocated
);
2134 fprintf(dbg
, "entry: start_line=%p, ypos=%d\n", cur
->start_line
,
2136 fprintf(dbg
, "entry: string=");
2137 if (cur
->cur_line
->string
== NULL
)
2138 fprintf(dbg
, "(null)\n");
2140 fprintf(dbg
, "\"%s\"\n", cur
->cur_line
->string
);
2143 /* Cannot manipulate a null string! */
2144 if (cur
->cur_line
->string
== NULL
)
2145 return E_REQUEST_DENIED
;
2148 row
= cur
->cur_line
;
2151 case REQ_RIGHT_CHAR
:
2153 * The right_char request performs the same function
2154 * as the next_char request except that the cursor is
2155 * not wrapped if it is at the end of the line, so
2156 * check if the cursor is at the end of the line and
2157 * deny the request otherwise just fall through to
2158 * the next_char request handler.
2160 if (cur
->cursor_xpos
>= cur
->cols
- 1)
2161 return E_REQUEST_DENIED
;
2166 /* for a dynamic field allow an offset of one more
2167 * char so we can insert chars after end of string.
2168 * Static fields cannot do this so deny request if
2169 * cursor is at the end of the field.
2171 if (((cur
->opts
& O_STATIC
) == O_STATIC
) &&
2172 (cur
->row_xpos
== cur
->cols
- 1) &&
2173 ((cur
->rows
+ cur
->nrows
) == 1))
2174 return E_REQUEST_DENIED
;
2176 if (((cur
->rows
+ cur
->nrows
) == 1) &&
2177 (cur
->row_xpos
+ cur
->start_char
+ 1) > row
->length
)
2178 return E_REQUEST_DENIED
;
2180 if ((cur
->rows
+ cur
->nrows
) == 1) {
2182 _formi_set_cursor_xpos(cur
, (c
== REQ_RIGHT_CHAR
));
2184 if (cur
->cursor_xpos
>= (row
->expanded
- 1)) {
2185 if ((row
->next
== NULL
) ||
2186 (c
== REQ_RIGHT_CHAR
))
2187 return E_REQUEST_DENIED
;
2189 cur
->cursor_xpos
= 0;
2191 cur
->cur_line
= cur
->cur_line
->next
;
2192 if (cur
->cursor_ypos
== (cur
->rows
- 1))
2194 cur
->start_line
->next
;
2198 old_xpos
= cur
->cursor_xpos
;
2199 old_row_pos
= cur
->row_xpos
;
2200 if (row
->string
[cur
->row_xpos
] == '\t')
2201 cur
->cursor_xpos
+= tab_size(row
,
2206 if (cur
->cursor_xpos
2208 if ((row
->next
== NULL
) ||
2209 (c
== REQ_RIGHT_CHAR
)) {
2210 cur
->cursor_xpos
= old_xpos
;
2211 cur
->row_xpos
= old_row_pos
;
2212 return E_REQUEST_DENIED
;
2215 cur
->cursor_xpos
= 0;
2217 cur
->cur_line
= cur
->cur_line
->next
;
2218 if (cur
->cursor_ypos
2221 cur
->start_line
->next
;
2232 * The behaviour of left_char is the same as prev_char
2233 * except that the cursor will not wrap if it has
2234 * reached the LHS of the field, so just check this
2235 * and fall through if we are not at the LHS.
2237 if (cur
->cursor_xpos
== 0)
2238 return E_REQUEST_DENIED
;
2242 if ((cur
->rows
+ cur
->nrows
) == 1) {
2243 if (cur
->row_xpos
== 0) {
2244 if (cur
->start_char
> 0)
2247 return E_REQUEST_DENIED
;
2250 _formi_set_cursor_xpos(cur
, FALSE
);
2253 if ((cur
->cursor_xpos
== 0) &&
2254 (cur
->cursor_ypos
== 0) &&
2255 (cur
->start_line
->prev
== NULL
))
2256 return E_REQUEST_DENIED
;
2258 pos
= cur
->row_xpos
;
2259 if (cur
->cursor_xpos
> 0) {
2260 if (row
->string
[pos
] == '\t') {
2261 size
= tab_size(row
, pos
);
2262 if (size
> cur
->cursor_xpos
) {
2263 cur
->cursor_xpos
= 0;
2267 cur
->cursor_xpos
-= size
;
2274 cur
->cur_line
= cur
->cur_line
->prev
;
2275 if (cur
->cursor_ypos
> 0)
2279 cur
->start_line
->prev
;
2280 row
= cur
->cur_line
;
2281 if (row
->expanded
> 0) {
2282 cur
->cursor_xpos
= row
->expanded
- 1;
2284 cur
->cursor_xpos
= 0;
2287 if (row
->length
> 0)
2288 cur
->row_xpos
= row
->length
- 1;
2298 * The down_char request has the same functionality as
2299 * the next_line request excepting that the field is not
2300 * scrolled if the cursor is at the bottom of the field.
2301 * Check to see if the cursor is at the bottom of the field
2302 * and if it is then deny the request otherwise fall
2303 * through to the next_line handler.
2305 if (cur
->cursor_ypos
>= cur
->rows
- 1)
2306 return E_REQUEST_DENIED
;
2311 if ((row
->next
== NULL
) || (cur
->cur_line
->next
== NULL
))
2312 return E_REQUEST_DENIED
;
2314 cur
->cur_line
= cur
->cur_line
->next
;
2315 if ((cur
->cursor_ypos
+ 1) >= cur
->rows
) {
2316 cur
->start_line
= cur
->start_line
->next
;
2319 row
= cur
->cur_line
;
2321 if (row
->length
== 0) {
2323 cur
->cursor_xpos
= 0;
2325 if (cur
->cursor_xpos
> (row
->expanded
- 1))
2326 cur
->cursor_xpos
= row
->expanded
- 1;
2328 cur
->row_xpos
= tab_fit_len(row
, cur
->cursor_xpos
+ 1);
2329 if (cur
->row_xpos
== 0)
2330 cur
->cursor_xpos
= 0;
2333 _formi_tab_expanded_length(
2334 row
->string
, 0, cur
->row_xpos
);
2335 if (cur
->cursor_xpos
> 0)
2342 * The up_char request has the same functionality as
2343 * the prev_line request excepting the field is not
2344 * scrolled, check if the cursor is at the top of the
2345 * field, if it is deny the request otherwise fall
2346 * through to the prev_line handler.
2348 if (cur
->cursor_ypos
== 0)
2349 return E_REQUEST_DENIED
;
2354 if (cur
->cur_line
->prev
== NULL
)
2355 return E_REQUEST_DENIED
;
2357 if (cur
->cursor_ypos
== 0) {
2358 if (cur
->start_line
->prev
== NULL
)
2359 return E_REQUEST_DENIED
;
2360 cur
->start_line
= cur
->start_line
->prev
;
2364 cur
->cur_line
= cur
->cur_line
->prev
;
2365 row
= cur
->cur_line
;
2367 if (row
->length
== 0) {
2369 cur
->cursor_xpos
= 0;
2371 if (cur
->cursor_xpos
> (row
->expanded
- 1))
2372 cur
->cursor_xpos
= row
->expanded
- 1;
2374 cur
->row_xpos
= tab_fit_len(row
, cur
->cursor_xpos
+ 1);
2376 _formi_tab_expanded_length(row
->string
,
2378 if (cur
->cursor_xpos
> 0)
2384 start
= cur
->row_xpos
+ cur
->start_char
;
2387 wb
= find_eow(cur
, start
, FALSE
, &row
);
2392 /* check if we hit the end */
2393 if (str
[start
] == '\0')
2394 return E_REQUEST_DENIED
;
2396 /* otherwise we must have found the start of a word...*/
2397 if ((cur
->rows
+ cur
->nrows
) == 1) {
2398 /* single line field */
2399 size
= _formi_tab_expanded_length(str
,
2400 cur
->start_char
, start
);
2401 if (size
< cur
->cols
) {
2402 cur
->row_xpos
= start
- cur
->start_char
;
2404 cur
->start_char
= start
;
2407 _formi_set_cursor_xpos(cur
, FALSE
);
2409 /* multiline field */
2410 cur
->cur_line
= row
;
2411 adjust_ypos(cur
, row
);
2413 cur
->row_xpos
= start
;
2415 _formi_tab_expanded_length(
2416 row
->string
, 0, cur
->row_xpos
) - 1;
2421 start
= cur
->start_char
+ cur
->row_xpos
;
2422 if (cur
->start_char
> 0)
2425 if ((start
== 0) && (row
->prev
== NULL
))
2426 return E_REQUEST_DENIED
;
2430 if (row
->length
> 0)
2431 start
= row
->length
- 1;
2438 start
= find_sow(start
, &row
);
2440 if ((cur
->rows
+ cur
->nrows
) == 1) {
2441 /* single line field */
2442 size
= _formi_tab_expanded_length(str
,
2443 cur
->start_char
, start
);
2445 if (start
> cur
->start_char
) {
2446 cur
->row_xpos
= start
- cur
->start_char
;
2448 cur
->start_char
= start
;
2451 _formi_set_cursor_xpos(cur
, FALSE
);
2453 /* multiline field */
2454 cur
->cur_line
= row
;
2455 adjust_ypos(cur
, row
);
2456 cur
->row_xpos
= start
;
2458 _formi_tab_expanded_length(
2466 cur
->start_char
= 0;
2467 while (cur
->start_line
->prev
!= NULL
)
2468 cur
->start_line
= cur
->start_line
->prev
;
2469 cur
->cur_line
= cur
->start_line
;
2471 _formi_init_field_xpos(cur
);
2472 cur
->cursor_ypos
= 0;
2477 _formi_init_field_xpos(cur
);
2478 cur
->start_char
= 0;
2482 while (cur
->cur_line
->next
!= NULL
)
2483 cur
->cur_line
= cur
->cur_line
->next
;
2485 if (cur
->row_count
> cur
->rows
) {
2486 cur
->start_line
= cur
->cur_line
;
2487 pos
= cur
->rows
- 1;
2489 cur
->start_line
= cur
->start_line
->prev
;
2492 cur
->cursor_ypos
= cur
->rows
- 1;
2494 cur
->cursor_ypos
= cur
->row_count
- 1;
2497 /* we fall through here deliberately, we are on the
2498 * correct row, now we need to get to the end of the
2504 row
= cur
->cur_line
;
2506 if ((cur
->rows
+ cur
->nrows
) == 1) {
2507 if (row
->expanded
> cur
->cols
- 1) {
2508 if ((cur
->opts
& O_STATIC
) != O_STATIC
) {
2509 cur
->start_char
= tab_fit_window(
2512 cur
->row_xpos
= row
->length
2515 cur
->start_char
= 0;
2516 cur
->row_xpos
= cur
->cols
- 1;
2519 cur
->row_xpos
= row
->length
+ 1;
2520 cur
->start_char
= 0;
2522 _formi_set_cursor_xpos(cur
, FALSE
);
2524 cur
->row_xpos
= row
->length
- 1;
2525 cur
->cursor_xpos
= row
->expanded
- 1;
2526 if (row
->next
== NULL
) {
2534 start
= cur
->start_char
+ cur
->row_xpos
;
2535 if ((status
= split_line(cur
, TRUE
, start
, &row
)) != E_OK
)
2537 cur
->cur_line
->hard_ret
= TRUE
;
2538 cur
->cursor_xpos
= 0;
2543 if ((status
= _formi_add_char(cur
, cur
->start_char
2550 if ((status
= split_line(cur
, TRUE
, 0, &row
)) != E_OK
)
2552 cur
->cur_line
->hard_ret
= TRUE
;
2556 row
= cur
->cur_line
;
2557 start
= cur
->start_char
+ cur
->row_xpos
;
2558 end
= row
->length
- 1;
2559 if ((start
>= row
->length
) && (row
->next
== NULL
))
2560 return E_REQUEST_DENIED
;
2562 if ((start
== row
->length
- 1) || (row
->length
== 0)) {
2563 if ((cur
->rows
+ cur
->nrows
) > 1) {
2565 * Firstly, check if the current line has
2566 * a hard return. In this case we just
2567 * want to "delete" the hard return and
2568 * re-wrap the field. The hard return
2569 * does not occupy a character space in
2570 * the buffer but we must make it appear
2571 * like it does for a deletion.
2573 if (row
->hard_ret
== TRUE
) {
2574 row
->hard_ret
= FALSE
;
2575 if (_formi_join_line(cur
, &row
,
2578 row
->hard_ret
= TRUE
;
2586 * If we have more than one row, join the
2587 * next row to make things easier unless
2588 * we are at the end of the string, in
2589 * that case the join would fail but we
2590 * really want to delete the last char
2593 if (row
->next
!= NULL
) {
2594 if (_formi_join_line(cur
, &row
,
2597 return E_REQUEST_DENIED
;
2603 saved
= row
->string
[start
];
2604 bcopy(&row
->string
[start
+ 1], &row
->string
[start
],
2605 (size_t) (end
- start
+ 1));
2606 row
->string
[end
] = '\0';
2608 if (row
->length
> 0)
2609 row
->expanded
= _formi_tab_expanded_length(
2610 row
->string
, 0, row
->length
- 1);
2615 * recalculate tabs for a single line field, multiline
2616 * fields will do this when the field is wrapped.
2618 if ((cur
->rows
+ cur
->nrows
) == 1)
2619 _formi_calculate_tabs(row
);
2621 * if we are at the end of the string then back the
2622 * cursor pos up one to stick on the end of the line
2624 if (start
== row
->length
) {
2625 if (row
->length
> 1) {
2626 if ((cur
->rows
+ cur
->nrows
) == 1) {
2627 pos
= cur
->row_xpos
+ cur
->start_char
;
2631 cur
->start_char
+ cur
->row_xpos
,
2633 cur
->row_xpos
= pos
- cur
->start_char
2635 _formi_set_cursor_xpos(cur
, FALSE
);
2637 if (cur
->row_xpos
== 0) {
2638 if (row
->next
!= NULL
) {
2639 if (_formi_join_line(
2643 return E_REQUEST_DENIED
;
2646 if (cur
->row_count
> 1)
2652 cur
->row_xpos
= start
- 1;
2654 _formi_tab_expanded_length(
2656 0, cur
->row_xpos
- 1);
2657 if ((cur
->cursor_xpos
> 0)
2658 && (start
!= (row
->expanded
- 1)))
2666 _formi_init_field_xpos(cur
);
2670 if ((cur
->rows
+ cur
->nrows
) > 1) {
2671 if (_formi_wrap_field(cur
, row
) != E_OK
) {
2672 bcopy(&row
->string
[start
],
2673 &row
->string
[start
+ 1],
2674 (size_t) (end
- start
));
2676 row
->string
[start
] = saved
;
2677 _formi_wrap_field(cur
, row
);
2678 return E_REQUEST_DENIED
;
2684 if ((cur
->cursor_xpos
== 0) && (cur
->start_char
== 0)
2685 && (cur
->start_line
->prev
== NULL
)
2686 && (cur
->cursor_ypos
== 0))
2687 return E_REQUEST_DENIED
;
2689 row
= cur
->cur_line
;
2690 start
= cur
->row_xpos
+ cur
->start_char
;
2691 end
= row
->length
- 1;
2694 if ((cur
->start_char
+ cur
->row_xpos
) == 0) {
2695 if (row
->prev
== NULL
)
2696 return E_REQUEST_DENIED
;
2699 * If we are a multiline field then check if
2700 * the line above has a hard return. If it does
2701 * then just "eat" the hard return and re-wrap
2704 if (row
->prev
->hard_ret
== TRUE
) {
2705 row
->prev
->hard_ret
= FALSE
;
2706 if (_formi_join_line(cur
, &row
,
2707 JOIN_PREV
) != E_OK
) {
2708 row
->prev
->hard_ret
= TRUE
;
2714 start
= row
->prev
->length
;
2716 * Join this line to the previous
2719 if (_formi_join_line(cur
, &row
,
2720 JOIN_PREV_NW
) != E_OK
) {
2723 end
= row
->length
- 1;
2727 if (eat_char
== TRUE
) {
2729 * eat a char from the buffer. Normally we do
2730 * this unless we have deleted a "hard return"
2731 * in which case we just want to join the lines
2732 * without losing a char.
2734 saved
= row
->string
[start
- 1];
2735 bcopy(&row
->string
[start
], &row
->string
[start
- 1],
2736 (size_t) (end
- start
+ 1));
2738 row
->string
[row
->length
] = '\0';
2739 row
->expanded
= _formi_tab_expanded_length(
2740 row
->string
, 0, row
->length
- 1);
2743 if ((cur
->rows
+ cur
->nrows
) == 1) {
2744 _formi_calculate_tabs(row
);
2745 pos
= cur
->row_xpos
+ cur
->start_char
;
2750 cur
->start_char
+ cur
->row_xpos
,
2752 cur
->row_xpos
= pos
- cur
->start_char
;
2753 _formi_set_cursor_xpos(cur
, FALSE
);
2755 if (eat_char
== TRUE
) {
2757 if (cur
->row_xpos
> 0)
2759 _formi_tab_expanded_length(
2763 cur
->cursor_xpos
= 0;
2766 if ((_formi_wrap_field(cur
, row
) != E_OK
)) {
2767 bcopy(&row
->string
[start
- 1],
2768 &row
->string
[start
],
2769 (size_t) (end
- start
));
2771 row
->string
[start
- 1] = saved
;
2772 row
->string
[row
->length
] = '\0';
2773 _formi_wrap_field(cur
, row
);
2774 return E_REQUEST_DENIED
;
2780 if (((cur
->rows
+ cur
->nrows
) == 1) ||
2781 (cur
->row_count
== 1)) {
2782 /* single line case */
2784 row
->expanded
= row
->length
= 0;
2786 _formi_init_field_xpos(cur
);
2787 cur
->cursor_ypos
= 0;
2789 /* multiline field */
2790 old_count
= cur
->row_count
;
2792 if (cur
->row_count
== 0)
2795 if (old_count
== 1) {
2796 row
->expanded
= row
->length
= 0;
2797 cur
->cursor_xpos
= 0;
2799 cur
->cursor_ypos
= 0;
2801 add_to_free(cur
, row
);
2803 if (row
->next
== NULL
) {
2804 if (cur
->cursor_ypos
== 0) {
2805 if (cur
->start_line
->prev
!= NULL
) {
2807 cur
->start_line
->prev
;
2814 if (old_count
> 1) {
2815 if (cur
->cursor_xpos
> row
->expanded
) {
2816 cur
->cursor_xpos
= row
->expanded
- 1;
2817 cur
->row_xpos
= row
->length
- 1;
2820 cur
->start_line
= cur
->alines
;
2821 rs
= cur
->start_line
;
2822 cur
->cursor_ypos
= 0;
2824 if (cur
->cursor_ypos
< cur
->rows
)
2828 cur
->start_line
->next
;
2836 start
= cur
->start_char
+ cur
->row_xpos
;
2839 wb
= find_eow(cur
, start
, TRUE
, &row
);
2846 * If not at the start of a word then find the start,
2847 * we cannot blindly call find_sow because this will
2848 * skip back a word if we are already at the start of
2852 && !(isblank((unsigned char)str
[start
- 1]) &&
2853 !isblank((unsigned char)str
[start
])))
2854 start
= find_sow(start
, &row
);
2856 /* XXXX hmmmm what if start and end on diff rows? XXXX */
2857 bcopy(&str
[end
], &str
[start
],
2858 (size_t) (row
->length
- end
+ 1));
2862 if ((cur
->rows
+ cur
->nrows
) > 1) {
2863 row
= cur
->start_line
+ cur
->cursor_ypos
;
2864 if (row
->next
!= NULL
) {
2866 * if not on the last row we need to
2867 * join on the next row so the line
2868 * will be re-wrapped.
2870 _formi_join_line(cur
, &row
, JOIN_NEXT_NW
);
2872 _formi_wrap_field(cur
, row
);
2873 cur
->row_xpos
= start
;
2874 cur
->cursor_xpos
= _formi_tab_expanded_length(
2875 row
->string
, 0, cur
->row_xpos
);
2876 if (cur
->cursor_xpos
> 0)
2879 _formi_calculate_tabs(row
);
2880 cur
->row_xpos
= start
- cur
->start_char
;
2881 if (cur
->row_xpos
> 0)
2883 _formi_set_cursor_xpos(cur
, FALSE
);
2888 row
->string
[cur
->row_xpos
+ 1] = '\0';
2889 row
->length
= cur
->row_xpos
+ 1;
2890 row
->expanded
= cur
->cursor_xpos
+ 1;
2894 row
= cur
->cur_line
->next
;
2895 while (row
!= NULL
) {
2897 add_to_free(cur
, row
);
2904 row
= cur
->alines
->next
;
2905 cur
->cur_line
= cur
->alines
;
2906 cur
->start_line
= cur
->alines
;
2908 while (row
!= NULL
) {
2910 add_to_free(cur
, row
);
2914 cur
->alines
->string
[0] = '\0';
2915 cur
->alines
->length
= 0;
2916 cur
->alines
->expanded
= 0;
2918 cur
->cursor_ypos
= 0;
2920 _formi_init_field_xpos(cur
);
2921 cur
->start_char
= 0;
2933 _formi_scroll_fwd(cur
, 1);
2937 _formi_scroll_back(cur
, 1);
2941 _formi_scroll_fwd(cur
, cur
->rows
);
2945 _formi_scroll_back(cur
, cur
->rows
);
2948 case REQ_SCR_FHPAGE
:
2949 _formi_scroll_fwd(cur
, cur
->rows
/ 2);
2952 case REQ_SCR_BHPAGE
:
2953 _formi_scroll_back(cur
, cur
->rows
/ 2);
2957 _formi_hscroll_fwd(cur
, row
, 1);
2961 _formi_hscroll_back(cur
, row
, 1);
2964 case REQ_SCR_HFLINE
:
2965 _formi_hscroll_fwd(cur
, row
, cur
->cols
);
2968 case REQ_SCR_HBLINE
:
2969 _formi_hscroll_back(cur
, row
, cur
->cols
);
2972 case REQ_SCR_HFHALF
:
2973 _formi_hscroll_fwd(cur
, row
, cur
->cols
/ 2);
2976 case REQ_SCR_HBHALF
:
2977 _formi_hscroll_back(cur
, row
, cur
->cols
/ 2);
2986 "exit: cursor_xpos=%d, row_xpos=%d, start_char=%d, length=%d, allocated=%d\n",
2987 cur
->cursor_xpos
, cur
->row_xpos
, cur
->start_char
,
2988 cur
->cur_line
->length
, cur
->cur_line
->allocated
);
2989 fprintf(dbg
, "exit: start_line=%p, ypos=%d\n", cur
->start_line
,
2991 fprintf(dbg
, "exit: string=\"%s\"\n", cur
->cur_line
->string
);
2992 assert ((cur
->cursor_xpos
< INT_MAX
) && (cur
->row_xpos
< INT_MAX
)
2993 && (cur
->cursor_xpos
>= cur
->row_xpos
));
2999 * Validate the given character by passing it to any type character
3000 * checking routines, if they exist.
3003 _formi_validate_char(FIELD
*field
, char c
)
3007 if (field
->type
== NULL
)
3010 ret_val
= E_INVALID_FIELD
;
3011 _formi_do_char_validation(field
, field
->type
, c
, &ret_val
);
3018 * Perform the validation of the character, invoke all field_type validation
3019 * routines. If the field is ok then update ret_val to E_OK otherwise
3020 * ret_val is not changed.
3023 _formi_do_char_validation(FIELD
*field
, FIELDTYPE
*type
, char c
, int *ret_val
)
3025 if ((type
->flags
& _TYPE_IS_LINKED
) == _TYPE_IS_LINKED
) {
3026 _formi_do_char_validation(field
, type
->link
->next
, c
, ret_val
);
3027 _formi_do_char_validation(field
, type
->link
->prev
, c
, ret_val
);
3029 if (type
->char_check
== NULL
)
3032 if (type
->char_check((int)(unsigned char) c
,
3033 field
->args
) == TRUE
)
3040 * Validate the current field. If the field validation returns success then
3041 * return E_OK otherwise return E_INVALID_FIELD.
3045 _formi_validate_field(FORM
*form
)
3051 if ((form
== NULL
) || (form
->fields
== NULL
) ||
3052 (form
->fields
[0] == NULL
))
3053 return E_INVALID_FIELD
;
3055 cur
= form
->fields
[form
->cur_field
];
3058 * Sync the buffer if it has been modified so the field
3059 * validation routines can use it and because this is
3060 * the correct behaviour according to AT&T implementation.
3062 if ((cur
->buf0_status
== TRUE
)
3063 && ((ret_val
= _formi_sync_buffer(cur
)) != E_OK
))
3067 * If buffer is untouched then the string pointer may be
3068 * NULL, see if this is ok or not.
3070 if (cur
->buffers
[0].string
== NULL
) {
3071 if ((cur
->opts
& O_NULLOK
) == O_NULLOK
)
3074 return E_INVALID_FIELD
;
3077 count
= _formi_skip_blanks(cur
->buffers
[0].string
, 0);
3079 /* check if we have a null field, depending on the nullok flag
3080 * this may be acceptable or not....
3082 if (cur
->buffers
[0].string
[count
] == '\0') {
3083 if ((cur
->opts
& O_NULLOK
) == O_NULLOK
)
3086 return E_INVALID_FIELD
;
3089 /* check if an unmodified field is ok */
3090 if (cur
->buf0_status
== 0) {
3091 if ((cur
->opts
& O_PASSOK
) == O_PASSOK
)
3094 return E_INVALID_FIELD
;
3097 /* if there is no type then just accept the field */
3098 if (cur
->type
== NULL
)
3101 ret_val
= E_INVALID_FIELD
;
3102 _formi_do_validation(cur
, cur
->type
, &ret_val
);
3108 * Perform the validation of the field, invoke all field_type validation
3109 * routines. If the field is ok then update ret_val to E_OK otherwise
3110 * ret_val is not changed.
3113 _formi_do_validation(FIELD
*field
, FIELDTYPE
*type
, int *ret_val
)
3115 if ((type
->flags
& _TYPE_IS_LINKED
) == _TYPE_IS_LINKED
) {
3116 _formi_do_validation(field
, type
->link
->next
, ret_val
);
3117 _formi_do_validation(field
, type
->link
->prev
, ret_val
);
3119 if (type
->field_check
== NULL
)
3122 if (type
->field_check(field
, field_buffer(field
, 0))
3130 * Select the next/previous choice for the field, the driver command
3131 * selecting the direction will be passed in c. Return 1 if a choice
3132 * selection succeeded, 0 otherwise.
3135 _formi_field_choice(FORM
*form
, int c
)
3140 if ((form
== NULL
) || (form
->fields
== NULL
) ||
3141 (form
->fields
[0] == NULL
) ||
3142 (form
->fields
[form
->cur_field
]->type
== NULL
))
3145 field
= form
->fields
[form
->cur_field
];
3149 case REQ_NEXT_CHOICE
:
3150 if (type
->next_choice
== NULL
)
3153 return type
->next_choice(field
,
3154 field_buffer(field
, 0));
3156 case REQ_PREV_CHOICE
:
3157 if (type
->prev_choice
== NULL
)
3160 return type
->prev_choice(field
,
3161 field_buffer(field
, 0));
3163 default: /* should never happen! */
3169 * Update the fields if they have changed. The parameter old has the
3170 * previous current field as the current field may have been updated by
3171 * the driver. Return 1 if the form page needs updating.
3175 _formi_update_field(FORM
*form
, int old_field
)
3179 cur
= form
->cur_field
;
3181 if (old_field
!= cur
) {
3182 if (!((cur
>= form
->page_starts
[form
->page
].first
) &&
3183 (cur
<= form
->page_starts
[form
->page
].last
))) {
3184 /* not on same page any more */
3185 for (i
= 0; i
< form
->max_page
; i
++) {
3186 if ((form
->page_starts
[i
].in_use
== 1) &&
3187 (form
->page_starts
[i
].first
<= cur
) &&
3188 (form
->page_starts
[i
].last
>= cur
)) {
3196 _formi_redraw_field(form
, old_field
);
3197 _formi_redraw_field(form
, form
->cur_field
);
3202 * Compare function for the field sorting
3206 field_sort_compare(const void *one
, const void *two
)
3211 /* LINTED const castaway; we don't modify these! */
3212 a
= (const FIELD
*) *((const FIELD
**) one
);
3213 b
= (const FIELD
*) *((const FIELD
**) two
);
3222 * First check the page, we want the fields sorted by page.
3225 if (a
->page
!= b
->page
)
3226 return ((a
->page
> b
->page
)? 1 : -1);
3228 tl
= _formi_top_left(a
->parent
, a
->index
, b
->index
);
3231 * sort fields left to right, top to bottom so the top left is
3232 * the lesser value....
3234 return ((tl
== a
->index
)? -1 : 1);
3238 * Sort the fields in a form ready for driver traversal.
3241 _formi_sort_fields(FORM
*form
)
3246 TAILQ_INIT(&form
->sorted_fields
);
3248 if ((sort_area
= malloc(sizeof(*sort_area
) * form
->field_count
))
3252 bcopy(form
->fields
, sort_area
,
3253 (size_t) (sizeof(FIELD
*) * form
->field_count
));
3254 qsort(sort_area
, (size_t) form
->field_count
, sizeof(FIELD
*),
3255 field_sort_compare
);
3257 for (i
= 0; i
< form
->field_count
; i
++)
3258 TAILQ_INSERT_TAIL(&form
->sorted_fields
, sort_area
[i
], glue
);
3264 * Set the neighbours for all the fields in the given form.
3267 _formi_stitch_fields(FORM
*form
)
3269 int above_row
, below_row
, end_above
, end_below
, cur_row
, real_end
;
3270 FIELD
*cur
, *above
, *below
;
3273 * check if the sorted fields circle queue is empty, just
3276 if (TAILQ_EMPTY(&form
->sorted_fields
))
3279 /* initially nothing is above..... */
3284 /* set up the first field as the current... */
3285 cur
= TAILQ_FIRST(&form
->sorted_fields
);
3286 cur_row
= cur
->form_row
;
3288 /* find the first field on the next row if any */
3289 below
= TAILQ_NEXT(cur
, glue
);
3293 while (below
!= NULL
) {
3294 if (below
->form_row
!= cur_row
) {
3295 below_row
= below
->form_row
;
3300 below
= TAILQ_NEXT(below
, glue
);
3303 /* walk the sorted fields, setting the neighbour pointers */
3304 while (cur
!= NULL
) {
3305 if (cur
== TAILQ_FIRST(&form
->sorted_fields
))
3308 cur
->left
= TAILQ_PREV(cur
, _formi_sort_head
, glue
);
3310 if (cur
== TAILQ_LAST(&form
->sorted_fields
, _formi_sort_head
))
3313 cur
->right
= TAILQ_NEXT(cur
, glue
);
3315 if (end_above
== TRUE
)
3319 above
= TAILQ_NEXT(above
, glue
);
3320 if (above_row
!= above
->form_row
) {
3322 above_row
= above
->form_row
;
3326 if (end_below
== TRUE
)
3330 below
= TAILQ_NEXT(below
, glue
);
3331 if (below
== NULL
) {
3334 } else if (below_row
!= below
->form_row
) {
3336 below_row
= below
->form_row
;
3340 cur
= TAILQ_NEXT(cur
, glue
);
3342 && (cur_row
!= cur
->form_row
)) {
3343 cur_row
= cur
->form_row
;
3344 if (end_above
== FALSE
) {
3346 TAILQ_FIRST(&form
->sorted_fields
);
3347 above
= TAILQ_NEXT(above
, glue
)) {
3348 if (above
->form_row
!= above_row
) {
3349 above_row
= above
->form_row
;
3353 } else if (above
== NULL
) {
3354 above
= TAILQ_FIRST(&form
->sorted_fields
);
3356 above_row
= above
->form_row
;
3360 if (end_below
== FALSE
) {
3361 while (below_row
== below
->form_row
) {
3362 below
= TAILQ_NEXT(below
, glue
);
3363 if (below
== NULL
) {
3371 below_row
= below
->form_row
;
3372 } else if (real_end
== FALSE
)
3380 * Calculate the length of the displayed line allowing for any tab
3381 * characters that need to be expanded. We assume that the tab stops
3382 * are 8 characters apart. The parameters start and end are the
3383 * character positions in the string str we want to get the length of,
3384 * the function returns the number of characters from the start
3385 * position to the end position that should be displayed after any
3386 * intervening tabs have been expanded.
3389 _formi_tab_expanded_length(char *str
, unsigned int start
, unsigned int end
)
3391 int len
, start_len
, i
;
3393 /* if we have a null string then there is no length */
3401 * preceding tabs affect the length tabs in the span, so
3402 * we need to calculate the length including the stuff before
3403 * start and then subtract off the unwanted bit.
3405 for (i
= 0; i
<= end
; i
++) {
3406 if (i
== start
) /* stash preamble length for later */
3413 len
= len
- (len
% 8) + 8;
3421 "tab_expanded: start=%d, end=%d, expanded=%d (diff=%d)\n",
3422 start
, end
, (len
- start_len
), (end
- start
));
3426 return (len
- start_len
);
3430 * Calculate the tab stops on a given line in the field and set up
3431 * the tabs list with the results. We do this by scanning the line for tab
3432 * characters and if one is found, noting the position and the number of
3433 * characters to get to the next tab stop. This information is kept to
3434 * make manipulating the field (scrolling and so on) easier to handle.
3437 _formi_calculate_tabs(_FORMI_FIELD_LINES
*row
)
3439 _formi_tab_t
*ts
= row
->tabs
, *old_ts
= NULL
, **tsp
;
3443 * If the line already has tabs then invalidate them by
3444 * walking the list and killing the in_use flag.
3446 for (; ts
!= NULL
; ts
= ts
->fwd
)
3451 * Now look for tabs in the row and record the info...
3454 for (i
= 0, j
= 0; i
< row
->length
; i
++, j
++) {
3455 if (row
->string
[i
] == '\t') {
3457 if ((*tsp
= (_formi_tab_t
*)
3458 malloc(sizeof(_formi_tab_t
))) == NULL
)
3460 (*tsp
)->back
= old_ts
;
3464 (*tsp
)->in_use
= TRUE
;
3466 (*tsp
)->size
= 8 - (j
% 8);
3467 j
+= (*tsp
)->size
- 1;
3475 * Return the size of the tab padding for a tab character at the given
3476 * position. Return 1 if there is not a tab char entry matching the
3480 tab_size(_FORMI_FIELD_LINES
*row
, unsigned int i
)
3485 while ((ts
!= NULL
) && (ts
->pos
!= i
))
3495 * Find the character offset that corresponds to longest tab expanded
3496 * string that will fit into the given window. Walk the string backwards
3497 * evaluating the sizes of any tabs that are in the string. Note that
3498 * using this function on a multi-line window will produce undefined
3499 * results - it is really only required for a single row field.
3502 tab_fit_window(FIELD
*field
, unsigned int pos
, unsigned int window
)
3507 /* first find the last tab */
3508 ts
= field
->alines
->tabs
;
3511 * unless there are no tabs - just return the window size,
3512 * if there is enough room, otherwise 0.
3515 if (field
->alines
->length
< window
)
3518 return field
->alines
->length
- window
+ 1;
3521 while ((ts
->fwd
!= NULL
) && (ts
->fwd
->in_use
== TRUE
))
3525 * now walk backwards finding the first tab that is to the
3526 * left of our starting pos.
3528 while ((ts
!= NULL
) && (ts
->in_use
== TRUE
) && (ts
->pos
> pos
))
3532 for (i
= pos
; i
>= 0; i
--) {
3533 if (field
->alines
->string
[i
] == '\t') {
3534 assert((ts
!= NULL
) && (ts
->in_use
== TRUE
));
3536 if ((scroll_amt
+ ts
->size
) > window
) {
3539 scroll_amt
+= ts
->size
;
3543 assert(ts
->pos
== i
);
3546 if (scroll_amt
> window
)
3555 * Return the position of the last character that will fit into the
3556 * given width after tabs have been expanded for a given row of a given
3560 tab_fit_len(_FORMI_FIELD_LINES
*row
, unsigned int width
)
3562 unsigned int pos
, len
, row_pos
;
3573 while ((len
< width
) && (pos
< row
->length
)) {
3574 if (row
->string
[pos
] == '\t') {
3575 assert((ts
!= NULL
) && (ts
->in_use
== TRUE
));
3576 if (ts
->pos
== row_pos
) {
3577 if ((len
+ ts
->size
) > width
)
3583 assert(ts
->pos
== row_pos
);
3596 * Sync the field line structures with the contents of buffer 0 for that
3597 * field. We do this by walking all the line structures and concatenating
3598 * all the strings into one single string in buffer 0.
3601 _formi_sync_buffer(FIELD
*field
)
3603 _FORMI_FIELD_LINES
*line
;
3607 if (field
->alines
== NULL
)
3608 return E_BAD_ARGUMENT
;
3610 if (field
->alines
->string
== NULL
)
3611 return E_BAD_ARGUMENT
;
3614 * init nstr up front, just in case there are no line contents,
3615 * this could happen if the field just contains hard returns.
3617 if ((nstr
= malloc(sizeof(char))) == NULL
)
3618 return E_SYSTEM_ERROR
;
3621 line
= field
->alines
;
3622 length
= 1; /* allow for terminating null */
3624 while (line
!= NULL
) {
3625 if (line
->length
!= 0) {
3626 if ((tmp
= realloc(nstr
,
3627 (size_t) (length
+ line
->length
)))
3631 return (E_SYSTEM_ERROR
);
3635 strcat(nstr
, line
->string
);
3636 length
+= line
->length
;
3642 if (field
->buffers
[0].string
!= NULL
)
3643 free(field
->buffers
[0].string
);
3644 field
->buffers
[0].allocated
= length
;
3645 field
->buffers
[0].length
= length
- 1;
3646 field
->buffers
[0].string
= nstr
;