Remove building with NOCRYPTO option
[minix.git] / lib / libform / internals.c
blob0b7b49bdd52906cc6b4c4b254bb1040c108c33e9
1 /* $NetBSD: internals.c,v 1.37 2013/11/26 01:17:00 christos Exp $ */
3 /*-
4 * Copyright (c) 1998-1999 Brett Lymn
5 * (blymn@baea.com.au, brett_lymn@yahoo.com.au)
6 * All rights reserved.
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
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission
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 $");
35 #include <limits.h>
36 #include <ctype.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <strings.h>
40 #include <assert.h>
41 #include "internals.h"
42 #include "form.h"
44 #ifdef DEBUG
46 * file handle to write debug info to, this will be initialised when
47 * the form is first posted.
49 FILE *dbg = NULL;
52 * map the request numbers to strings for debug
54 char *reqs[] = {
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" };
67 #endif
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
71 * from min(x++, y++)
73 #define min(a,b) (((a) > (b))? (b) : (a))
75 /* for the line joining function... */
76 #define JOIN_NEXT 1
77 #define JOIN_NEXT_NW 2 /* next join, don't wrap the joined line */
78 #define JOIN_PREV 3
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
87 unsigned
88 field_skip_blanks(unsigned int start, _FORMI_FIELD_LINES **rowp);
89 static void
90 _formi_do_char_validation(FIELD *field, FIELDTYPE *type, char c, int *ret_val);
91 static void
92 _formi_do_validation(FIELD *field, FIELDTYPE *type, int *ret_val);
93 static int
94 _formi_join_line(FIELD *field, _FORMI_FIELD_LINES **rowp, int direction);
95 void
96 _formi_hscroll_back(FIELD *field, _FORMI_FIELD_LINES *row, unsigned int amt);
97 void
98 _formi_hscroll_fwd(FIELD *field, _FORMI_FIELD_LINES *row, unsigned int amt);
99 static void
100 _formi_scroll_back(FIELD *field, unsigned int amt);
101 static void
102 _formi_scroll_fwd(FIELD *field, unsigned int amt);
103 static int
104 _formi_set_cursor_xpos(FIELD *field, int no_scroll);
105 static int
106 find_sow(unsigned int offset, _FORMI_FIELD_LINES **rowp);
107 static int
108 split_line(FIELD *field, bool hard_split, unsigned pos,
109 _FORMI_FIELD_LINES **rowp);
110 static bool
111 check_field_size(FIELD *field);
112 static int
113 add_tab(FORM *form, _FORMI_FIELD_LINES *row, unsigned int i, char c);
114 static int
115 tab_size(_FORMI_FIELD_LINES *row, unsigned int i);
116 static unsigned int
117 tab_fit_len(_FORMI_FIELD_LINES *row, unsigned int len);
118 static int
119 tab_fit_window(FIELD *field, unsigned int pos, unsigned int window);
120 static void
121 add_to_free(FIELD *field, _FORMI_FIELD_LINES *line);
122 static void
123 adjust_ypos(FIELD *field, _FORMI_FIELD_LINES *line);
124 static _FORMI_FIELD_LINES *
125 copy_row(_FORMI_FIELD_LINES *row);
126 static void
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.
135 static void
136 adjust_ypos(FIELD *field, _FORMI_FIELD_LINES *line)
138 unsigned ypos;
139 _FORMI_FIELD_LINES *rs;
141 ypos = 0;
142 rs = field->alines;
143 while (rs != line) {
144 rs = rs->next;
145 ypos++;
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);
157 while (ypos > 0) {
158 ypos--;
159 field->start_line = field->start_line->next;
166 * Delete the given row and add it to the free list of the given field.
168 static void
169 add_to_free(FIELD *field, _FORMI_FIELD_LINES *line)
171 _FORMI_FIELD_LINES *saved;
173 saved = line;
175 /* don't remove if only one line... */
176 if ((line->prev == NULL) && (line->next == NULL))
177 return;
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;
195 } else {
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;
205 field->free = saved;
206 saved->prev = NULL;
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)))
222 == NULL) {
223 return NULL;
226 memcpy(new, row, sizeof(_FORMI_FIELD_LINES));
228 /* nuke the pointers from the source row so we don't get confused */
229 new->next = NULL;
230 new->prev = NULL;
231 new->tabs = NULL;
233 if ((new->string = (char *) malloc((size_t)new->allocated)) == NULL) {
234 free(new);
235 return NULL;
238 memcpy(new->string, row->string, (size_t) row->length + 1);
240 if (row->tabs != NULL) {
241 tp = row->tabs;
242 if ((new->tabs = (_formi_tab_t *) malloc(sizeof(_formi_tab_t)))
243 == NULL) {
244 free(new->string);
245 free(new);
246 return NULL;
249 memcpy(new->tabs, row->tabs, sizeof(_formi_tab_t));
250 new->tabs->back = NULL;
251 new->tabs->fwd = NULL;
253 tp = tp->fwd;
254 newt = new->tabs;
256 while (tp != NULL) {
257 if ((newt->fwd =
258 (_formi_tab_t *) malloc(sizeof(_formi_tab_t)))
259 == NULL) {
260 /* error... unwind allocations */
261 tp = new->tabs;
262 while (tp != NULL) {
263 newt = tp->fwd;
264 free(tp);
265 tp = newt;
268 free(new->string);
269 free(new);
270 return NULL;
273 memcpy(newt->fwd, tp, sizeof(_formi_tab_t));
274 newt->fwd->back = newt;
275 newt = newt->fwd;
276 newt->fwd = NULL;
277 tp = tp->fwd;
281 return new;
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.
290 void
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;
297 return;
300 switch (field->justification) {
301 case JUSTIFY_RIGHT:
302 field->cursor_xpos = field->cols - 1;
303 break;
305 case JUSTIFY_CENTER:
306 field->cursor_xpos = (field->cols - 1) / 2;
307 break;
309 default: /* assume left justify */
310 field->cursor_xpos = 0;
311 break;
317 * Open the debug file if it is not already open....
319 #ifdef DEBUG
321 _formi_create_dbg_file(void)
323 if (dbg == NULL) {
324 dbg = fopen("___form_dbg.out", "w");
325 if (dbg == NULL) {
326 fprintf(stderr, "Cannot open debug file!\n");
327 return E_SYSTEM_ERROR;
331 return E_OK;
333 #endif
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.
345 static bool
346 check_field_size(FIELD *field)
348 if ((field->opts & O_STATIC) != O_STATIC) {
349 /* dynamic field */
350 if (field->max == 0) /* unlimited */
351 return TRUE;
353 if (field->rows == 1) {
354 return (field->alines->length < field->max);
355 } else {
356 return (field->row_count <= field->max);
358 } else {
359 if ((field->rows + field->nrows) == 1) {
360 return (field->alines->length <= field->cols);
361 } else {
362 return (field->row_count <= (field->rows
363 + field->nrows));
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)
375 FIELD *cur;
376 int old_page;
378 old_page = form->page;
380 /* scan forward for an active page....*/
381 while (form->page_starts[form->page].in_use == 0) {
382 form->page++;
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);
394 if (cur == NULL) {
395 form->page = old_page;
396 return E_REQUEST_DENIED;
400 form->cur_field = cur->index;
401 return E_OK;
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)
413 FIELD *cur;
414 int i;
416 i = form->cur_field;
417 cur = form->fields[i];
419 do {
420 if (direction == _FORMI_FORWARD) {
421 if (use_sorted == TRUE) {
422 if ((form->wrap == FALSE) &&
423 (cur == TAILQ_LAST(&form->sorted_fields,
424 _formi_sort_head)))
425 return E_REQUEST_DENIED;
426 cur = TAILQ_NEXT(cur, glue);
427 i = cur->index;
428 } else {
429 if ((form->wrap == FALSE) &&
430 ((i + 1) >= form->field_count))
431 return E_REQUEST_DENIED;
432 i++;
433 if (i >= form->field_count)
434 i = 0;
436 } else {
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);
442 i = cur->index;
443 } else {
444 if ((form->wrap == FALSE) && (i <= 0))
445 return E_REQUEST_DENIED;
446 i--;
447 if (i < 0)
448 i = form->field_count - 1;
452 if ((form->fields[i]->opts & (O_VISIBLE | O_ACTIVE))
453 == (O_VISIBLE | O_ACTIVE)) {
454 form->cur_field = i;
455 return E_OK;
458 while (i != form->cur_field);
460 return E_REQUEST_DENIED;
464 * Destroy the list of line structs passed by freeing all allocated
465 * memory.
467 static void
468 destroy_row_list(_FORMI_FIELD_LINES *start)
470 _FORMI_FIELD_LINES *temp, *row;
471 _formi_tab_t *tt, *tp;
473 row = start;
474 while (row != NULL) {
475 if (row->tabs != NULL) {
476 /* free up the tab linked list... */
477 tp = row->tabs;
478 while (tp != NULL) {
479 tt = tp->fwd;
480 free(tp);
481 tp = tt;
485 if (row->string != NULL)
486 free(row->string);
488 temp = row->next;
489 free(row);
490 row = temp;
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)
503 int width, wrap_err;
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 */
513 width = field->cols;
514 } else {
515 /* if we are limited to one line then don't try to wrap */
516 if ((field->drows + field->nrows) == 1) {
517 return E_OK;
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) {
527 width = field->cols;
528 } else {
529 return E_OK;
533 row = loc;
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
537 * the one above.
539 if (row->prev != NULL)
540 row = row->prev;
542 saved_row = row;
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
550 * don't work out.
552 if ((row_backup = copy_row(row)) == NULL)
553 return E_SYSTEM_ERROR;
555 temp = row_backup;
556 row = row->next;
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;
569 temp = temp->next;
571 if (row == field->start_line)
572 saved_start_line = temp;
573 if (row == field->cur_line)
574 saved_cur_line = temp;
576 row = row->next;
579 row = saved_row;
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
588 * anything to it.
590 row = row->next;
591 continue;
594 if (row->next == NULL) {
596 * If there are no more lines and this line
597 * is too short then our job is over.
599 break;
602 if (_formi_join_line(field, &row,
603 JOIN_NEXT_NW) == E_OK) {
604 continue;
605 } else
606 break;
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) {
616 pos = width - 1;
617 if (pos >= row->expanded)
618 pos = row->expanded - 1;
619 } else {
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,
627 &row);
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)
633 if ((pos == 0)
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)
646 continue;
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))
656 pos++;
658 if (split_line(field, FALSE, pos, &row) != E_OK) {
659 wrap_err = E_REQUEST_DENIED;
660 goto restore_and_exit;
662 } else
663 /* line is exactly the right length, do next one */
664 row = row->next;
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;
674 restore_and_exit:
675 if (saved_row->prev == NULL) {
676 field->alines = row_backup;
677 } else {
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);
690 return wrap_err;
693 destroy_row_list(row_backup);
694 return E_OK;
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.
705 static int
706 _formi_join_line(FIELD *field, _FORMI_FIELD_LINES **rowp, int direction)
708 int old_len, count;
709 struct _formi_field_lines *saved;
710 char *newp;
711 _FORMI_FIELD_LINES *row = *rowp;
712 #ifdef DEBUG
713 int dbg_ok = FALSE;
715 if (_formi_create_dbg_file() == E_OK) {
716 dbg_ok = TRUE;
719 if (dbg_ok == TRUE) {
720 fprintf(dbg, "join_line: working on row %p, row_count = %d\n",
721 row, field->row_count);
723 #endif
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
729 * any lines to it.
731 if ((row->next == NULL) || (row->hard_ret == TRUE)) {
732 return E_REQUEST_DENIED;
735 #ifdef DEBUG
736 if (dbg_ok == TRUE) {
737 fprintf(dbg,
738 "join_line: join_next before length = %d, expanded = %d",
739 row->length, row->expanded);
740 fprintf(dbg,
741 " :: next row length = %d, expanded = %d\n",
742 row->length, row->expanded);
744 #endif
746 if (row->allocated < (row->length + row->next->length + 1)) {
747 if ((newp = realloc(row->string, (size_t)(row->length +
748 row->next->length
749 + 1))) == NULL)
750 return E_REQUEST_DENIED;
751 row->string = newp;
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;
758 if (row->length > 0)
759 row->expanded =
760 _formi_tab_expanded_length(row->string, 0,
761 row->length - 1);
762 else
763 row->expanded = 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;
772 field->cursor_xpos =
773 _formi_tab_expanded_length(row->string, 0,
774 field->row_xpos);
775 if (field->cursor_xpos > 0)
776 field->cursor_xpos--;
778 if (field->cursor_ypos > 0)
779 field->cursor_ypos--;
780 else {
781 if (field->start_line->prev != NULL)
782 field->start_line =
783 field->start_line->prev;
787 /* remove joined line record from the row list */
788 add_to_free(field, row->next);
790 #ifdef DEBUG
791 if (dbg_ok == TRUE) {
792 fprintf(dbg,
793 "join_line: exit length = %d, expanded = %d\n",
794 row->length, row->expanded);
796 #endif
797 } else {
798 if (row->prev == NULL) {
799 return E_REQUEST_DENIED;
802 saved = row->prev;
805 * Don't try to join if the line above has a hard
806 * return on it.
808 if (saved->hard_ret == TRUE) {
809 return E_REQUEST_DENIED;
812 #ifdef DEBUG
813 if (dbg_ok == TRUE) {
814 fprintf(dbg,
815 "join_line: join_prev before length = %d, expanded = %d",
816 row->length, row->expanded);
817 fprintf(dbg,
818 " :: prev row length = %d, expanded = %d\n",
819 saved->length, saved->expanded);
821 #endif
823 if (saved->allocated < (row->length + saved->length + 1)) {
824 if ((newp = realloc(saved->string,
825 (size_t) (row->length +
826 saved->length
827 + 1))) == NULL)
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)
837 saved->expanded =
838 _formi_tab_expanded_length(saved->string, 0,
839 saved->length - 1);
840 else
841 saved->length = 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;
849 field->cursor_xpos =
850 _formi_tab_expanded_length(saved->string, 0,
851 field->row_xpos);
852 if (field->cursor_xpos > 0)
853 field->cursor_xpos--;
856 add_to_free(field, row);
858 #ifdef DEBUG
859 if (dbg_ok == TRUE) {
860 fprintf(dbg,
861 "join_line: exit length = %d, expanded = %d\n",
862 saved->length, saved->expanded);
864 #endif
865 row = saved;
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;
875 count = 0;
876 while (saved->next != NULL) {
877 if (saved == row)
878 break;
879 count++;
880 saved = saved->next;
883 /* now check if we need to adjust cursor_ypos */
884 if (field->cursor_ypos > count) {
885 field->cursor_ypos--;
888 field->row_count--;
889 *rowp = row;
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;
898 return E_OK;
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.
906 static int
907 split_line(FIELD *field, bool hard_split, unsigned pos,
908 _FORMI_FIELD_LINES **rowp)
910 struct _formi_field_lines *new_line;
911 char *newp;
912 _FORMI_FIELD_LINES *row = *rowp;
913 #ifdef DEBUG
914 short dbg_ok = FALSE;
915 #endif
917 /* if asked to split right where the line already starts then
918 * just return - nothing to do unless we are appending a line
919 * to the buffer.
921 if ((pos == 0) && (hard_split == FALSE))
922 return E_OK;
924 #ifdef DEBUG
925 if (_formi_create_dbg_file() == E_OK) {
926 fprintf(dbg, "split_line: splitting line at %d\n", pos);
927 dbg_ok = TRUE;
929 #endif
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;
937 } else {
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;
951 #ifdef DEBUG
952 if (dbg_ok == TRUE) {
953 fprintf(dbg,
954 "split_line: enter: length = %d, expanded = %d\n",
955 row->length, row->expanded);
957 #endif
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]);
980 row->length = pos;
981 row->string[pos] = '\0';
983 if (row->length != 0)
984 row->expanded = _formi_tab_expanded_length(row->string, 0,
985 row->length - 1);
986 else
987 row->expanded = 0;
988 _formi_calculate_tabs(row);
990 if (new_line->length != 0)
991 new_line->expanded =
992 _formi_tab_expanded_length(new_line->string, 0,
993 new_line->length - 1);
994 else
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,
1008 field->row_xpos);
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;
1018 else
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)));
1046 #ifdef DEBUG
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);
1051 fprintf(dbg,
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);
1056 #endif
1058 field->row_count++;
1059 *rowp = new_line;
1061 return E_OK;
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.
1070 unsigned
1071 _formi_skip_blanks(char *string, unsigned int start)
1073 unsigned int i;
1075 i = start;
1077 while ((string[i] != '\0') && isblank((unsigned char)string[i]))
1078 i++;
1080 return 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.
1090 unsigned
1091 field_skip_blanks(unsigned int start, _FORMI_FIELD_LINES **rowp)
1093 unsigned int i;
1094 _FORMI_FIELD_LINES *row, *last = NULL;
1096 row = *rowp;
1097 i = start;
1099 do {
1100 i = _formi_skip_blanks(&row->string[i], i);
1101 if (!isblank((unsigned char)row->string[i])) {
1102 last = row;
1103 row = row->next;
1105 * don't reset if last line otherwise we will
1106 * not be at the end of the string.
1108 if (row != NULL)
1109 i = 0;
1110 } else
1111 break;
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.
1119 if (row == NULL)
1120 *rowp = last;
1121 else
1122 *rowp = row;
1124 return i;
1128 * Return the index of the top left most field of the two given fields.
1130 static int
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)
1135 return a;
1137 if (form->fields[a]->form_row > form->fields[b]->form_row)
1138 return b;
1140 /* rows must be equal, check columns */
1141 if (form->fields[a]->form_col < form->fields[b]->form_col)
1142 return a;
1144 if (form->fields[a]->form_col > form->fields[b]->form_col)
1145 return b;
1147 /* if we get here fields must be in exactly the same place, punt */
1148 return a;
1152 * Return the index to the field that is the bottom-right-most of the
1153 * two given fields.
1155 static int
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)
1160 return a;
1161 if (form->fields[a]->form_row < form->fields[b]->form_row)
1162 return b;
1164 /* rows must be equal, check cols, biggest wins */
1165 if (form->fields[a]->form_col > form->fields[b]->form_col)
1166 return a;
1167 if (form->fields[a]->form_col < form->fields[b]->form_col)
1168 return b;
1170 /* fields in the same place, punt */
1171 return a;
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).
1182 static int
1183 find_eow(FIELD *cur, unsigned int offset, bool do_join,
1184 _FORMI_FIELD_LINES **rowp)
1186 int start;
1187 _FORMI_FIELD_LINES *row;
1189 row = *rowp;
1190 start = offset;
1192 do {
1193 /* first skip any non-whitespace */
1194 while ((row->string[start] != '\0')
1195 && !isblank((unsigned char)row->string[start]))
1196 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)
1202 return start;
1204 if (_formi_join_line(cur, &row, JOIN_NEXT_NW)
1205 != E_OK)
1206 return E_REQUEST_DENIED;
1207 } else {
1208 do {
1209 if (row->next == NULL) {
1210 *rowp = row;
1211 return start;
1212 } else {
1213 row = row->next;
1214 start = 0;
1216 } while (row->length == 0);
1219 } while (!isblank((unsigned char)row->string[start]));
1221 do {
1222 /* otherwise skip the whitespace.... */
1223 while ((row->string[start] != '\0')
1224 && isblank((unsigned char)row->string[start]))
1225 start++;
1227 if (row->string[start] == '\0') {
1228 if (do_join == TRUE) {
1229 if (row->next == NULL)
1230 return start;
1232 if (_formi_join_line(cur, &row, JOIN_NEXT_NW)
1233 != E_OK)
1234 return E_REQUEST_DENIED;
1235 } else {
1236 do {
1237 if (row->next == NULL) {
1238 *rowp = row;
1239 return start;
1240 } else {
1241 row = row->next;
1242 start = 0;
1244 } while (row->length == 0);
1247 } while (isblank((unsigned char)row->string[start]));
1249 *rowp = row;
1250 return start;
1254 * Find the beginning of the current word in the string str, starting
1255 * at offset.
1257 static int
1258 find_sow(unsigned int offset, _FORMI_FIELD_LINES **rowp)
1260 int start;
1261 char *str;
1262 _FORMI_FIELD_LINES *row;
1264 row = *rowp;
1265 str = row->string;
1266 start = offset;
1268 do {
1269 if (start > 0) {
1270 if (isblank((unsigned char)str[start]) ||
1271 isblank((unsigned char)str[start - 1])) {
1272 if (isblank((unsigned char)str[start - 1]))
1273 start--;
1274 /* skip the whitespace.... */
1275 while ((start >= 0) &&
1276 isblank((unsigned char)str[start]))
1277 start--;
1281 /* see if we hit the start of the string */
1282 if (start < 0) {
1283 do {
1284 if (row->prev == NULL) {
1285 *rowp = row;
1286 start = 0;
1287 return start;
1288 } else {
1289 row = row->prev;
1290 str = row->string;
1291 if (row->length > 0)
1292 start = row->length - 1;
1293 else
1294 start = 0;
1296 } while (row->length == 0);
1298 } while (isblank((unsigned char)row->string[start]));
1300 /* see if we hit the start of the string */
1301 if (start < 0) {
1302 *rowp = row;
1303 return 0;
1306 /* now skip any non-whitespace */
1307 do {
1308 while ((start >= 0) && !isblank((unsigned char)str[start]))
1309 start--;
1312 if (start < 0) {
1313 do {
1314 if (row->prev == NULL) {
1315 *rowp = row;
1316 start = 0;
1317 return start;
1318 } else {
1319 row = row->prev;
1320 str = row->string;
1321 if (row->length > 0)
1322 start = row->length - 1;
1323 else
1324 start = 0;
1326 } while (row->length == 0);
1328 } while (!isblank((unsigned char)str[start]));
1330 if (start > 0) {
1331 start++; /* last loop has us pointing at a space, adjust */
1332 if (start >= row->length) {
1333 if (row->next != NULL) {
1334 start = 0;
1335 row = row->next;
1336 } else {
1337 start = row->length - 1;
1342 if (start < 0)
1343 start = 0;
1345 *rowp = row;
1346 return start;
1350 * Scroll the field forward the given number of lines.
1352 static void
1353 _formi_scroll_fwd(FIELD *field, unsigned int amt)
1355 unsigned int count;
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))
1363 count--;
1364 end_row = end_row->next;
1367 /* check if there are lines to scroll */
1368 if ((count > 0) && (end_row->next == NULL))
1369 return;
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.
1377 count = amt;
1378 while ((count > 0) && (end_row->next != NULL)) {
1379 count--;
1380 field->start_line = field->start_line->next;
1381 end_row = end_row->next;
1386 * Scroll the field backward the given number of lines.
1388 static void
1389 _formi_scroll_back(FIELD *field, unsigned int amt)
1391 unsigned int count;
1393 /* check for lines above */
1394 if (field->start_line->prev == NULL)
1395 return;
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.
1401 count = amt;
1402 while ((count > 0) && (field->start_line->prev != NULL)) {
1403 count--;
1404 field->start_line = field->start_line->prev;
1409 * Scroll the field forward the given number of characters.
1411 void
1412 _formi_hscroll_fwd(FIELD *field, _FORMI_FIELD_LINES *row, int unsigned amt)
1414 unsigned int end, scroll_amt, expanded;
1415 _formi_tab_t *ts;
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;
1421 scroll_amt = amt;
1422 if (end > row->length) {
1423 end = row->length;
1424 scroll_amt = end - field->start_char - field->cols + 1;
1426 } else {
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;
1437 else {
1438 expanded = _formi_tab_expanded_length(
1439 row->string,
1440 field->start_char + amt,
1441 field->start_char + field->row_xpos + amt);
1442 ts = row->tabs;
1443 /* skip tabs to the lhs of our starting point */
1444 while ((ts != NULL) && (ts->in_use == TRUE)
1445 && (ts->pos < end))
1446 ts = ts->fwd;
1448 while ((expanded <= field->cols)
1449 && (end < row->length)) {
1450 if (row->string[end] == '\t') {
1451 assert((ts != NULL)
1452 && (ts->in_use == TRUE));
1453 if (ts->pos == end) {
1454 if ((expanded + ts->size)
1455 > field->cols)
1456 break;
1457 expanded += ts->size;
1458 ts = ts->fwd;
1460 else
1461 assert(ts->pos == end);
1462 } else
1463 expanded++;
1464 end++;
1468 scroll_amt = tab_fit_window(field, end, field->cols);
1469 if (scroll_amt < field->start_char)
1470 scroll_amt = 1;
1471 else
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,
1480 field->start_char,
1481 field->row_xpos
1482 + field->start_char) - 1;
1487 * Scroll the field backward the given number of characters.
1489 void
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,
1495 field->row_xpos
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)
1522 cur_page++;
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;
1529 } else {
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,
1542 return E_OK;
1546 * Completely redraw the field of the given form.
1548 void
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;
1553 char *str, c;
1554 FIELD *cur;
1555 _FORMI_FIELD_LINES *row;
1556 #ifdef DEBUG
1557 char buffer[100];
1558 #endif
1560 cur = form->fields[field];
1561 flen = cur->cols;
1562 slen = 0;
1563 start = 0;
1564 line = 0;
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)
1572 len = row->length;
1573 else
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);
1578 else
1579 slen = 0;
1581 if (slen > cur->cols)
1582 slen = cur->cols;
1583 slen += cur->start_char;
1584 } else
1585 slen = row->expanded;
1587 if ((cur->opts & O_STATIC) == O_STATIC) {
1588 switch (cur->justification) {
1589 case JUSTIFY_RIGHT:
1590 post = 0;
1591 if (flen < slen)
1592 pre = 0;
1593 else
1594 pre = flen - slen;
1595 break;
1597 case JUSTIFY_CENTER:
1598 if (flen < slen) {
1599 pre = 0;
1600 post = 0;
1601 } else {
1602 pre = flen - slen;
1603 post = pre = pre / 2;
1604 /* get padding right if
1605 centring is not even */
1606 if ((post + pre + slen) < flen)
1607 post++;
1609 break;
1611 case NO_JUSTIFICATION:
1612 case JUSTIFY_LEFT:
1613 default:
1614 pre = 0;
1615 if (flen <= slen)
1616 post = 0;
1617 else {
1618 post = flen - slen;
1619 if (post > flen)
1620 post = flen;
1622 break;
1624 } else {
1625 /* dynamic fields are not justified */
1626 pre = 0;
1627 if (flen <= slen)
1628 post = 0;
1629 else {
1630 post = flen - slen;
1631 if (post > flen)
1632 post = flen;
1635 /* but they do scroll.... */
1637 if (pre > cur->start_char - start)
1638 pre = pre - cur->start_char + start;
1639 else
1640 pre = 0;
1642 if (slen > cur->start_char) {
1643 slen -= cur->start_char;
1644 if (slen > flen)
1645 post = 0;
1646 else
1647 post = flen - slen;
1649 if (post > flen)
1650 post = flen;
1651 } else {
1652 slen = 0;
1653 post = flen - pre;
1657 if (form->cur_field == field)
1658 wattrset(form->scrwin, cur->fore);
1659 else
1660 wattrset(form->scrwin, cur->back);
1662 str = &row->string[cur->start_char];
1664 #ifdef DEBUG
1665 if (_formi_create_dbg_file() == E_OK) {
1666 fprintf(dbg,
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);
1669 if (str != NULL) {
1670 if (row->expanded != 0) {
1671 strncpy(buffer, str, flen);
1672 } else {
1673 strcpy(buffer, "(empty)");
1675 } else {
1676 strcpy(buffer, "(null)");
1678 buffer[flen] = '\0';
1679 fprintf(dbg, "redraw_field: %s\n", buffer);
1681 #endif
1683 for (i = start + cur->start_char; i < pre; i++)
1684 waddch(form->scrwin, cur->pad);
1686 #ifdef DEBUG
1687 fprintf(dbg, "redraw_field: will add %d chars\n",
1688 min(slen, flen));
1689 #endif
1690 for (i = 0, cpos = cur->start_char; i < min(slen, flen);
1691 i++, str++, cpos++)
1693 c = *str;
1694 tab = 0; /* just to shut gcc up */
1695 #ifdef DEBUG
1696 fprintf(dbg, "adding char str[%d]=%c\n",
1697 cpos + cur->start_char, c);
1698 #endif
1699 if (((cur->opts & O_PUBLIC) != O_PUBLIC)) {
1700 if (c == '\t')
1701 tab = add_tab(form, row, cpos,
1702 cur->pad);
1703 else
1704 waddch(form->scrwin, cur->pad);
1705 } else if ((cur->opts & O_VISIBLE) == O_VISIBLE) {
1706 if (c == '\t')
1707 tab = add_tab(form, row, cpos, ' ');
1708 else
1709 waddch(form->scrwin, c);
1710 } else {
1711 if (c == '\t')
1712 tab = add_tab(form, row, cpos, ' ');
1713 else
1714 waddch(form->scrwin, ' ');
1718 * If we have had a tab then skip forward
1719 * the requisite number of chars to keep
1720 * things in sync.
1722 if (c == '\t')
1723 i += tab - 1;
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);
1736 else
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);
1745 return;
1749 * Add the correct number of the given character to simulate a tab
1750 * in the field.
1752 static int
1753 add_tab(FORM *form, _FORMI_FIELD_LINES *row, unsigned int i, char c)
1755 int j;
1756 _formi_tab_t *ts = row->tabs;
1758 while ((ts != NULL) && (ts->pos != i))
1759 ts = ts->fwd;
1761 assert(ts != NULL);
1763 for (j = 0; j < ts->size; j++)
1764 waddch(form->scrwin, c);
1766 return ts->size;
1771 * Display the fields attached to the form that are on the current page
1772 * on the screen.
1776 _formi_draw_page(FORM *form)
1778 int i;
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);
1789 return E_OK;
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)
1798 char *new, old_c;
1799 unsigned int new_size;
1800 int status;
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))
1811 == NULL)
1812 return E_SYSTEM_ERROR;
1813 row->string[0] = '\0';
1814 row->allocated = INITIAL_LINE_ALLOC;
1815 row->length = 0;
1816 row->expanded = 0;
1819 if (_formi_validate_char(field, c) != E_OK) {
1820 #ifdef DEBUG
1821 fprintf(dbg, "add_char: char %c failed char validation\n", c);
1822 #endif
1823 return E_INVALID_FIELD;
1826 if ((c == '\t') && (field->cols <= 8)) {
1827 #ifdef DEBUG
1828 fprintf(dbg, "add_char: field too small for a tab\n");
1829 #endif
1830 return E_NO_ROOM;
1833 #ifdef DEBUG
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);
1841 #endif
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 */
1848 temp = row->next;
1849 do {
1850 next_temp = temp->next;
1851 add_to_free(field, temp);
1852 temp = next_temp;
1853 } while (temp != NULL);
1856 row->length = 0;
1857 row->string[0] = '\0';
1858 pos = 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;
1865 row->expanded = 0;
1866 row->length = 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;
1877 if (row->length + 2
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;
1884 row->string = new;
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))) {
1908 row->length++;
1911 _formi_calculate_tabs(row);
1912 row->expanded = _formi_tab_expanded_length(row->string, 0,
1913 row->length - 1);
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
1932 * char insert
1934 bcopy(&row->string[pos + 1], &row->string[pos],
1935 (size_t) (row->length - pos));
1936 row->length--;
1937 if (pos > 0)
1938 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.
1952 if (status == E_OK)
1953 status = E_REQUEST_DENIED;
1955 } else {
1956 field->buf0_status = TRUE;
1957 field->row_xpos++;
1958 if ((field->rows + field->nrows) == 1) {
1959 status = _formi_set_cursor_xpos(field, FALSE);
1960 } else {
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
1970 * field.
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));
1983 #ifdef DEBUG
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");
1994 #endif
1995 return status;
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.
2004 static int
2005 _formi_set_cursor_xpos(FIELD *field, int noscroll)
2007 int just, pos;
2009 just = field->justification;
2010 pos = field->start_char + field->row_xpos;
2012 #ifdef DEBUG
2013 fprintf(dbg,
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);
2016 #endif
2019 * make sure we apply the correct justification to non-static
2020 * fields.
2022 if (((field->rows + field->nrows) != 1) ||
2023 ((field->opts & O_STATIC) != O_STATIC))
2024 just = JUSTIFY_LEFT;
2026 switch (just) {
2027 case JUSTIFY_RIGHT:
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,
2034 field->row_xpos);
2035 break;
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);
2047 break;
2049 default:
2050 field->cursor_xpos = _formi_tab_expanded_length(
2051 field->cur_line->string,
2052 field->start_char,
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;
2063 if (field->row_xpos
2064 == (field->cur_line->length - 1)) {
2065 field->cursor_xpos = field->cols - 1;
2066 } else {
2067 field->cursor_xpos =
2068 _formi_tab_expanded_length(
2069 field->cur_line->string,
2070 field->start_char,
2071 field->row_xpos
2072 + field->start_char
2073 - 1) - 1;
2075 } else {
2076 if (noscroll == FALSE) {
2077 field->start_char =
2078 tab_fit_window(
2079 field,
2080 field->start_char
2081 + field->row_xpos,
2082 field->cols);
2083 field->row_xpos = pos
2084 - field->start_char;
2085 field->cursor_xpos =
2086 _formi_tab_expanded_length(
2087 field->cur_line->string,
2088 field->start_char,
2089 field->row_xpos
2090 + field->start_char - 1);
2091 } else {
2092 field->cursor_xpos = (field->cols - 1);
2097 break;
2100 #ifdef DEBUG
2101 fprintf(dbg,
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);
2104 #endif
2105 return E_OK;
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)
2116 FIELD *cur;
2117 char *str, saved;
2118 unsigned int start, end, pos, status, old_count, size;
2119 unsigned int old_xpos, old_row_pos;
2120 int len, wb;
2121 bool eat_char;
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;
2128 #ifdef DEBUG
2129 fprintf(dbg, "entry: request is REQ_%s\n", reqs[c - REQ_MIN_REQUEST]);
2130 fprintf(dbg,
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,
2135 cur->cursor_ypos);
2136 fprintf(dbg, "entry: string=");
2137 if (cur->cur_line->string == NULL)
2138 fprintf(dbg, "(null)\n");
2139 else
2140 fprintf(dbg, "\"%s\"\n", cur->cur_line->string);
2141 #endif
2143 /* Cannot manipulate a null string! */
2144 if (cur->cur_line->string == NULL)
2145 return E_REQUEST_DENIED;
2147 saved = '\0';
2148 row = cur->cur_line;
2150 switch (c) {
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;
2163 /* FALLTHRU */
2165 case REQ_NEXT_CHAR:
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) {
2181 cur->row_xpos++;
2182 _formi_set_cursor_xpos(cur, (c == REQ_RIGHT_CHAR));
2183 } else {
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;
2190 cur->row_xpos = 0;
2191 cur->cur_line = cur->cur_line->next;
2192 if (cur->cursor_ypos == (cur->rows - 1))
2193 cur->start_line =
2194 cur->start_line->next;
2195 else
2196 cur->cursor_ypos++;
2197 } else {
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,
2202 cur->row_xpos);
2203 else
2204 cur->cursor_xpos++;
2205 cur->row_xpos++;
2206 if (cur->cursor_xpos
2207 >= row->expanded) {
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;
2216 cur->row_xpos = 0;
2217 cur->cur_line = cur->cur_line->next;
2218 if (cur->cursor_ypos
2219 == (cur->rows - 1))
2220 cur->start_line =
2221 cur->start_line->next;
2222 else
2223 cur->cursor_ypos++;
2228 break;
2230 case REQ_LEFT_CHAR:
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;
2240 /* FALLTHRU */
2241 case REQ_PREV_CHAR:
2242 if ((cur->rows + cur->nrows) == 1) {
2243 if (cur->row_xpos == 0) {
2244 if (cur->start_char > 0)
2245 cur->start_char--;
2246 else
2247 return E_REQUEST_DENIED;
2248 } else {
2249 cur->row_xpos--;
2250 _formi_set_cursor_xpos(cur, FALSE);
2252 } else {
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;
2264 cur->row_xpos = 0;
2265 } else {
2266 cur->row_xpos--;
2267 cur->cursor_xpos -= size;
2269 } else {
2270 cur->cursor_xpos--;
2271 cur->row_xpos--;
2273 } else {
2274 cur->cur_line = cur->cur_line->prev;
2275 if (cur->cursor_ypos > 0)
2276 cur->cursor_ypos--;
2277 else
2278 cur->start_line =
2279 cur->start_line->prev;
2280 row = cur->cur_line;
2281 if (row->expanded > 0) {
2282 cur->cursor_xpos = row->expanded - 1;
2283 } else {
2284 cur->cursor_xpos = 0;
2287 if (row->length > 0)
2288 cur->row_xpos = row->length - 1;
2289 else
2290 cur->row_xpos = 0;
2294 break;
2296 case REQ_DOWN_CHAR:
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;
2308 /* FALLTHRU */
2310 case REQ_NEXT_LINE:
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;
2317 } else
2318 cur->cursor_ypos++;
2319 row = cur->cur_line;
2321 if (row->length == 0) {
2322 cur->row_xpos = 0;
2323 cur->cursor_xpos = 0;
2324 } else {
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;
2331 else
2332 cur->cursor_xpos =
2333 _formi_tab_expanded_length(
2334 row->string, 0, cur->row_xpos);
2335 if (cur->cursor_xpos > 0)
2336 cur->cursor_xpos--;
2338 break;
2340 case REQ_UP_CHAR:
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;
2351 /* FALLTHRU */
2353 case REQ_PREV_LINE:
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;
2361 } else
2362 cur->cursor_ypos--;
2364 cur->cur_line = cur->cur_line->prev;
2365 row = cur->cur_line;
2367 if (row->length == 0) {
2368 cur->row_xpos = 0;
2369 cur->cursor_xpos = 0;
2370 } else {
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);
2375 cur->cursor_xpos =
2376 _formi_tab_expanded_length(row->string,
2377 0, cur->row_xpos);
2378 if (cur->cursor_xpos > 0)
2379 cur->cursor_xpos--;
2381 break;
2383 case REQ_NEXT_WORD:
2384 start = cur->row_xpos + cur->start_char;
2385 str = row->string;
2387 wb = find_eow(cur, start, FALSE, &row);
2388 if (wb < 0)
2389 return wb;
2391 start = wb;
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;
2403 } else {
2404 cur->start_char = start;
2405 cur->row_xpos = 0;
2407 _formi_set_cursor_xpos(cur, FALSE);
2408 } else {
2409 /* multiline field */
2410 cur->cur_line = row;
2411 adjust_ypos(cur, row);
2413 cur->row_xpos = start;
2414 cur->cursor_xpos =
2415 _formi_tab_expanded_length(
2416 row->string, 0, cur->row_xpos) - 1;
2418 break;
2420 case REQ_PREV_WORD:
2421 start = cur->start_char + cur->row_xpos;
2422 if (cur->start_char > 0)
2423 start--;
2425 if ((start == 0) && (row->prev == NULL))
2426 return E_REQUEST_DENIED;
2428 if (start == 0) {
2429 row = row->prev;
2430 if (row->length > 0)
2431 start = row->length - 1;
2432 else
2433 start = 0;
2436 str = row->string;
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;
2447 } else {
2448 cur->start_char = start;
2449 cur->row_xpos = 0;
2451 _formi_set_cursor_xpos(cur, FALSE);
2452 } else {
2453 /* multiline field */
2454 cur->cur_line = row;
2455 adjust_ypos(cur, row);
2456 cur->row_xpos = start;
2457 cur->cursor_xpos =
2458 _formi_tab_expanded_length(
2459 row->string, 0,
2460 cur->row_xpos) - 1;
2463 break;
2465 case REQ_BEG_FIELD:
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;
2470 cur->row_xpos = 0;
2471 _formi_init_field_xpos(cur);
2472 cur->cursor_ypos = 0;
2473 break;
2475 case REQ_BEG_LINE:
2476 cur->row_xpos = 0;
2477 _formi_init_field_xpos(cur);
2478 cur->start_char = 0;
2479 break;
2481 case REQ_END_FIELD:
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;
2488 while (pos > 0) {
2489 cur->start_line = cur->start_line->prev;
2490 pos--;
2492 cur->cursor_ypos = cur->rows - 1;
2493 } else {
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
2499 * line.
2501 /* FALLTHRU */
2503 case REQ_END_LINE:
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(
2510 cur, row->length,
2511 cur->cols) + 1;
2512 cur->row_xpos = row->length
2513 - cur->start_char;
2514 } else {
2515 cur->start_char = 0;
2516 cur->row_xpos = cur->cols - 1;
2518 } else {
2519 cur->row_xpos = row->length + 1;
2520 cur->start_char = 0;
2522 _formi_set_cursor_xpos(cur, FALSE);
2523 } else {
2524 cur->row_xpos = row->length - 1;
2525 cur->cursor_xpos = row->expanded - 1;
2526 if (row->next == NULL) {
2527 cur->row_xpos++;
2528 cur->cursor_xpos++;
2531 break;
2533 case REQ_NEW_LINE:
2534 start = cur->start_char + cur->row_xpos;
2535 if ((status = split_line(cur, TRUE, start, &row)) != E_OK)
2536 return status;
2537 cur->cur_line->hard_ret = TRUE;
2538 cur->cursor_xpos = 0;
2539 cur->row_xpos = 0;
2540 break;
2542 case REQ_INS_CHAR:
2543 if ((status = _formi_add_char(cur, cur->start_char
2544 + cur->row_xpos,
2545 cur->pad)) != E_OK)
2546 return status;
2547 break;
2549 case REQ_INS_LINE:
2550 if ((status = split_line(cur, TRUE, 0, &row)) != E_OK)
2551 return status;
2552 cur->cur_line->hard_ret = TRUE;
2553 break;
2555 case REQ_DEL_CHAR:
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,
2576 JOIN_NEXT)
2577 != E_OK) {
2578 row->hard_ret = TRUE;
2579 return 0;
2580 } else {
2581 return 1;
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
2591 * in the field.
2593 if (row->next != NULL) {
2594 if (_formi_join_line(cur, &row,
2595 JOIN_NEXT_NW)
2596 != E_OK) {
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';
2607 row->length--;
2608 if (row->length > 0)
2609 row->expanded = _formi_tab_expanded_length(
2610 row->string, 0, row->length - 1);
2611 else
2612 row->expanded = 0;
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;
2628 cur->start_char =
2629 tab_fit_window(
2630 cur,
2631 cur->start_char + cur->row_xpos,
2632 cur->cols);
2633 cur->row_xpos = pos - cur->start_char
2634 - 1;
2635 _formi_set_cursor_xpos(cur, FALSE);
2636 } else {
2637 if (cur->row_xpos == 0) {
2638 if (row->next != NULL) {
2639 if (_formi_join_line(
2640 cur, &row,
2641 JOIN_PREV_NW)
2642 != E_OK) {
2643 return E_REQUEST_DENIED;
2645 } else {
2646 if (cur->row_count > 1)
2647 cur->row_count--;
2652 cur->row_xpos = start - 1;
2653 cur->cursor_xpos =
2654 _formi_tab_expanded_length(
2655 row->string,
2656 0, cur->row_xpos - 1);
2657 if ((cur->cursor_xpos > 0)
2658 && (start != (row->expanded - 1)))
2659 cur->cursor_xpos--;
2662 start--;
2663 } else {
2664 start = 0;
2665 cur->row_xpos = 0;
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));
2675 row->length++;
2676 row->string[start] = saved;
2677 _formi_wrap_field(cur, row);
2678 return E_REQUEST_DENIED;
2681 break;
2683 case REQ_DEL_PREV:
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;
2692 eat_char = TRUE;
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
2702 * the field.
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;
2709 return 0;
2712 eat_char = FALSE;
2713 } else {
2714 start = row->prev->length;
2716 * Join this line to the previous
2717 * one.
2719 if (_formi_join_line(cur, &row,
2720 JOIN_PREV_NW) != E_OK) {
2721 return 0;
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));
2737 row->length--;
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;
2746 if (pos > 0)
2747 pos--;
2748 cur->start_char =
2749 tab_fit_window(cur,
2750 cur->start_char + cur->row_xpos,
2751 cur->cols);
2752 cur->row_xpos = pos - cur->start_char;
2753 _formi_set_cursor_xpos(cur, FALSE);
2754 } else {
2755 if (eat_char == TRUE) {
2756 cur->row_xpos--;
2757 if (cur->row_xpos > 0)
2758 cur->cursor_xpos =
2759 _formi_tab_expanded_length(
2760 row->string, 0,
2761 cur->row_xpos - 1);
2762 else
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));
2770 row->length++;
2771 row->string[start - 1] = saved;
2772 row->string[row->length] = '\0';
2773 _formi_wrap_field(cur, row);
2774 return E_REQUEST_DENIED;
2777 break;
2779 case REQ_DEL_LINE:
2780 if (((cur->rows + cur->nrows) == 1) ||
2781 (cur->row_count == 1)) {
2782 /* single line case */
2783 row->length = 0;
2784 row->expanded = row->length = 0;
2785 cur->row_xpos = 0;
2786 _formi_init_field_xpos(cur);
2787 cur->cursor_ypos = 0;
2788 } else {
2789 /* multiline field */
2790 old_count = cur->row_count;
2791 cur->row_count--;
2792 if (cur->row_count == 0)
2793 cur->row_count = 1;
2795 if (old_count == 1) {
2796 row->expanded = row->length = 0;
2797 cur->cursor_xpos = 0;
2798 cur->row_xpos = 0;
2799 cur->cursor_ypos = 0;
2800 } else
2801 add_to_free(cur, row);
2803 if (row->next == NULL) {
2804 if (cur->cursor_ypos == 0) {
2805 if (cur->start_line->prev != NULL) {
2806 cur->start_line =
2807 cur->start_line->prev;
2809 } else {
2810 cur->cursor_ypos--;
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;
2823 while (rs != row) {
2824 if (cur->cursor_ypos < cur->rows)
2825 cur->cursor_ypos++;
2826 else
2827 cur->start_line =
2828 cur->start_line->next;
2829 rs = rs->next;
2833 break;
2835 case REQ_DEL_WORD:
2836 start = cur->start_char + cur->row_xpos;
2837 str = row->string;
2839 wb = find_eow(cur, start, TRUE, &row);
2840 if (wb < 0)
2841 return wb;
2843 end = wb;
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
2849 * a word.
2851 if ((start > 0)
2852 && !(isblank((unsigned char)str[start - 1]) &&
2853 !isblank((unsigned char)str[start])))
2854 start = find_sow(start, &row);
2855 str = row->string;
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));
2859 len = end - start;
2860 row->length -= len;
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)
2877 cur->cursor_xpos--;
2878 } else {
2879 _formi_calculate_tabs(row);
2880 cur->row_xpos = start - cur->start_char;
2881 if (cur->row_xpos > 0)
2882 cur->row_xpos--;
2883 _formi_set_cursor_xpos(cur, FALSE);
2885 break;
2887 case REQ_CLR_EOL:
2888 row->string[cur->row_xpos + 1] = '\0';
2889 row->length = cur->row_xpos + 1;
2890 row->expanded = cur->cursor_xpos + 1;
2891 break;
2893 case REQ_CLR_EOF:
2894 row = cur->cur_line->next;
2895 while (row != NULL) {
2896 rs = row->next;
2897 add_to_free(cur, row);
2898 row = rs;
2899 cur->row_count--;
2901 break;
2903 case REQ_CLR_FIELD:
2904 row = cur->alines->next;
2905 cur->cur_line = cur->alines;
2906 cur->start_line = cur->alines;
2908 while (row != NULL) {
2909 rs = row->next;
2910 add_to_free(cur, row);
2911 row = rs;
2914 cur->alines->string[0] = '\0';
2915 cur->alines->length = 0;
2916 cur->alines->expanded = 0;
2917 cur->row_count = 1;
2918 cur->cursor_ypos = 0;
2919 cur->row_xpos = 0;
2920 _formi_init_field_xpos(cur);
2921 cur->start_char = 0;
2922 break;
2924 case REQ_OVL_MODE:
2925 cur->overlay = 1;
2926 break;
2928 case REQ_INS_MODE:
2929 cur->overlay = 0;
2930 break;
2932 case REQ_SCR_FLINE:
2933 _formi_scroll_fwd(cur, 1);
2934 break;
2936 case REQ_SCR_BLINE:
2937 _formi_scroll_back(cur, 1);
2938 break;
2940 case REQ_SCR_FPAGE:
2941 _formi_scroll_fwd(cur, cur->rows);
2942 break;
2944 case REQ_SCR_BPAGE:
2945 _formi_scroll_back(cur, cur->rows);
2946 break;
2948 case REQ_SCR_FHPAGE:
2949 _formi_scroll_fwd(cur, cur->rows / 2);
2950 break;
2952 case REQ_SCR_BHPAGE:
2953 _formi_scroll_back(cur, cur->rows / 2);
2954 break;
2956 case REQ_SCR_FCHAR:
2957 _formi_hscroll_fwd(cur, row, 1);
2958 break;
2960 case REQ_SCR_BCHAR:
2961 _formi_hscroll_back(cur, row, 1);
2962 break;
2964 case REQ_SCR_HFLINE:
2965 _formi_hscroll_fwd(cur, row, cur->cols);
2966 break;
2968 case REQ_SCR_HBLINE:
2969 _formi_hscroll_back(cur, row, cur->cols);
2970 break;
2972 case REQ_SCR_HFHALF:
2973 _formi_hscroll_fwd(cur, row, cur->cols / 2);
2974 break;
2976 case REQ_SCR_HBHALF:
2977 _formi_hscroll_back(cur, row, cur->cols / 2);
2978 break;
2980 default:
2981 return 0;
2984 #ifdef DEBUG
2985 fprintf(dbg,
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,
2990 cur->cursor_ypos);
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));
2994 #endif
2995 return 1;
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)
3005 int ret_val;
3007 if (field->type == NULL)
3008 return E_OK;
3010 ret_val = E_INVALID_FIELD;
3011 _formi_do_char_validation(field, field->type, c, &ret_val);
3013 return 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.
3022 static void
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);
3028 } else {
3029 if (type->char_check == NULL)
3030 *ret_val = E_OK;
3031 else {
3032 if (type->char_check((int)(unsigned char) c,
3033 field->args) == TRUE)
3034 *ret_val = E_OK;
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)
3047 FIELD *cur;
3048 int ret_val, count;
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))
3064 return ret_val;
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)
3072 return E_OK;
3073 else
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)
3084 return E_OK;
3085 else
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)
3092 return E_OK;
3093 else
3094 return E_INVALID_FIELD;
3097 /* if there is no type then just accept the field */
3098 if (cur->type == NULL)
3099 return E_OK;
3101 ret_val = E_INVALID_FIELD;
3102 _formi_do_validation(cur, cur->type, &ret_val);
3104 return 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.
3112 static void
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);
3118 } else {
3119 if (type->field_check == NULL)
3120 *ret_val = E_OK;
3121 else {
3122 if (type->field_check(field, field_buffer(field, 0))
3123 == TRUE)
3124 *ret_val = E_OK;
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)
3137 FIELDTYPE *type;
3138 FIELD *field;
3140 if ((form == NULL) || (form->fields == NULL) ||
3141 (form->fields[0] == NULL) ||
3142 (form->fields[form->cur_field]->type == NULL))
3143 return 0;
3145 field = form->fields[form->cur_field];
3146 type = field->type;
3148 switch (c) {
3149 case REQ_NEXT_CHOICE:
3150 if (type->next_choice == NULL)
3151 return 0;
3152 else
3153 return type->next_choice(field,
3154 field_buffer(field, 0));
3156 case REQ_PREV_CHOICE:
3157 if (type->prev_choice == NULL)
3158 return 0;
3159 else
3160 return type->prev_choice(field,
3161 field_buffer(field, 0));
3163 default: /* should never happen! */
3164 return 0;
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)
3177 int cur, i;
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)) {
3189 form->page = i;
3190 return 1;
3196 _formi_redraw_field(form, old_field);
3197 _formi_redraw_field(form, form->cur_field);
3198 return 0;
3202 * Compare function for the field sorting
3205 static int
3206 field_sort_compare(const void *one, const void *two)
3208 const FIELD *a, *b;
3209 int tl;
3211 /* LINTED const castaway; we don't modify these! */
3212 a = (const FIELD *) *((const FIELD **) one);
3213 b = (const FIELD *) *((const FIELD **) two);
3215 if (a == NULL)
3216 return 1;
3218 if (b == NULL)
3219 return -1;
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.
3240 void
3241 _formi_sort_fields(FORM *form)
3243 FIELD **sort_area;
3244 int i;
3246 TAILQ_INIT(&form->sorted_fields);
3248 if ((sort_area = malloc(sizeof(*sort_area) * form->field_count))
3249 == NULL)
3250 return;
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);
3260 free(sort_area);
3264 * Set the neighbours for all the fields in the given form.
3266 void
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
3274 * return if it is.
3276 if (TAILQ_EMPTY(&form->sorted_fields))
3277 return;
3279 /* initially nothing is above..... */
3280 above_row = -1;
3281 end_above = TRUE;
3282 above = NULL;
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);
3290 below_row = -1;
3291 end_below = TRUE;
3292 real_end = TRUE;
3293 while (below != NULL) {
3294 if (below->form_row != cur_row) {
3295 below_row = below->form_row;
3296 end_below = FALSE;
3297 real_end = FALSE;
3298 break;
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))
3306 cur->left = NULL;
3307 else
3308 cur->left = TAILQ_PREV(cur, _formi_sort_head, glue);
3310 if (cur == TAILQ_LAST(&form->sorted_fields, _formi_sort_head))
3311 cur->right = NULL;
3312 else
3313 cur->right = TAILQ_NEXT(cur, glue);
3315 if (end_above == TRUE)
3316 cur->up = NULL;
3317 else {
3318 cur->up = above;
3319 above = TAILQ_NEXT(above, glue);
3320 if (above_row != above->form_row) {
3321 end_above = TRUE;
3322 above_row = above->form_row;
3326 if (end_below == TRUE)
3327 cur->down = NULL;
3328 else {
3329 cur->down = below;
3330 below = TAILQ_NEXT(below, glue);
3331 if (below == NULL) {
3332 end_below = TRUE;
3333 real_end = TRUE;
3334 } else if (below_row != below->form_row) {
3335 end_below = TRUE;
3336 below_row = below->form_row;
3340 cur = TAILQ_NEXT(cur, glue);
3341 if ((cur != NULL)
3342 && (cur_row != cur->form_row)) {
3343 cur_row = cur->form_row;
3344 if (end_above == FALSE) {
3345 for (; above !=
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;
3350 break;
3353 } else if (above == NULL) {
3354 above = TAILQ_FIRST(&form->sorted_fields);
3355 end_above = FALSE;
3356 above_row = above->form_row;
3357 } else
3358 end_above = FALSE;
3360 if (end_below == FALSE) {
3361 while (below_row == below->form_row) {
3362 below = TAILQ_NEXT(below, glue);
3363 if (below == NULL) {
3364 real_end = TRUE;
3365 end_below = TRUE;
3366 break;
3370 if (below != NULL)
3371 below_row = below->form_row;
3372 } else if (real_end == FALSE)
3373 end_below = 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 */
3394 if (str[0] == '\0')
3395 return 0;
3397 len = 0;
3398 start_len = 0;
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 */
3407 start_len = len;
3409 if (str[i] == '\0')
3410 break;
3412 if (str[i] == '\t')
3413 len = len - (len % 8) + 8;
3414 else
3415 len++;
3418 #ifdef DEBUG
3419 if (dbg != NULL) {
3420 fprintf(dbg,
3421 "tab_expanded: start=%d, end=%d, expanded=%d (diff=%d)\n",
3422 start, end, (len - start_len), (end - start));
3424 #endif
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.
3436 void
3437 _formi_calculate_tabs(_FORMI_FIELD_LINES *row)
3439 _formi_tab_t *ts = row->tabs, *old_ts = NULL, **tsp;
3440 int i, j;
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)
3447 ts->in_use = FALSE;
3451 * Now look for tabs in the row and record the info...
3453 tsp = &row->tabs;
3454 for (i = 0, j = 0; i < row->length; i++, j++) {
3455 if (row->string[i] == '\t') {
3456 if (*tsp == NULL) {
3457 if ((*tsp = (_formi_tab_t *)
3458 malloc(sizeof(_formi_tab_t))) == NULL)
3459 return;
3460 (*tsp)->back = old_ts;
3461 (*tsp)->fwd = NULL;
3464 (*tsp)->in_use = TRUE;
3465 (*tsp)->pos = i;
3466 (*tsp)->size = 8 - (j % 8);
3467 j += (*tsp)->size - 1;
3468 old_ts = *tsp;
3469 tsp = &(*tsp)->fwd;
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
3477 * given location.
3479 static int
3480 tab_size(_FORMI_FIELD_LINES *row, unsigned int i)
3482 _formi_tab_t *ts;
3484 ts = row->tabs;
3485 while ((ts != NULL) && (ts->pos != i))
3486 ts = ts->fwd;
3488 if (ts == NULL)
3489 return 1;
3490 else
3491 return ts->size;
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.
3501 static int
3502 tab_fit_window(FIELD *field, unsigned int pos, unsigned int window)
3504 int scroll_amt, i;
3505 _formi_tab_t *ts;
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.
3514 if (ts == NULL) {
3515 if (field->alines->length < window)
3516 return 0;
3517 else
3518 return field->alines->length - window + 1;
3521 while ((ts->fwd != NULL) && (ts->fwd->in_use == TRUE))
3522 ts = ts->fwd;
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))
3529 ts = ts->back;
3531 scroll_amt = 0;
3532 for (i = pos; i >= 0; i--) {
3533 if (field->alines->string[i] == '\t') {
3534 assert((ts != NULL) && (ts->in_use == TRUE));
3535 if (ts->pos == i) {
3536 if ((scroll_amt + ts->size) > window) {
3537 break;
3539 scroll_amt += ts->size;
3540 ts = ts->back;
3542 else
3543 assert(ts->pos == i);
3544 } else {
3545 scroll_amt++;
3546 if (scroll_amt > window)
3547 break;
3551 return ++i;
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
3557 * field.
3559 static unsigned int
3560 tab_fit_len(_FORMI_FIELD_LINES *row, unsigned int width)
3562 unsigned int pos, len, row_pos;
3563 _formi_tab_t *ts;
3565 ts = row->tabs;
3566 pos = 0;
3567 len = 0;
3568 row_pos = 0;
3570 if (width == 0)
3571 return 0;
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)
3578 break;
3579 len += ts->size;
3580 ts = ts->fwd;
3582 else
3583 assert(ts->pos == row_pos);
3584 } else
3585 len++;
3586 pos++;
3587 row_pos++;
3590 if (pos > 0)
3591 pos--;
3592 return 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;
3604 char *nstr, *tmp;
3605 unsigned length;
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;
3619 nstr[0] = '\0';
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)))
3628 == NULL) {
3629 if (nstr != NULL)
3630 free(nstr);
3631 return (E_SYSTEM_ERROR);
3634 nstr = tmp;
3635 strcat(nstr, line->string);
3636 length += line->length;
3639 line = line->next;
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;
3647 return E_OK;