/* $NetBSD: internals.c,v 1.22 2001/08/05 01:51:23 blymn Exp $ */ /*- * Copyright (c) 1998-1999 Brett Lymn * (blymn@baea.com.au, brett_lymn@yahoo.com.au) * All rights reserved. * * This code has been donated to The NetBSD Foundation by the Author. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * */ #include #include #include #include #include #include #include "internals.h" #include "form.h" #ifdef DEBUG /* * file handle to write debug info to, this will be initialised when * the form is first posted. */ FILE *dbg = NULL; #endif /* define our own min function - this is not generic but will do here * (don't believe me? think about what value you would get * from min(x++, y++) */ #define min(a,b) (((a) > (b))? (b) : (a)) /* for the line joining function... */ #define JOIN_NEXT 1 #define JOIN_NEXT_NW 2 /* next join, don't wrap the joined line */ #define JOIN_PREV 3 #define JOIN_PREV_NW 4 /* previous join, don't wrap the joined line */ /* for the bump_lines function... */ #define _FORMI_USE_CURRENT -1 /* indicates current cursor pos to be used */ static void _formi_do_char_validation(FIELD *field, FIELDTYPE *type, char c, int *ret_val); static void _formi_do_validation(FIELD *field, FIELDTYPE *type, int *ret_val); static int _formi_join_line(FIELD *field, unsigned int pos, int direction); void _formi_hscroll_back(FIELD *field, unsigned int amt); void _formi_hscroll_fwd(FIELD *field, unsigned int amt); static void _formi_scroll_back(FIELD *field, unsigned int amt); static void _formi_scroll_fwd(FIELD *field, unsigned int amt); static int find_sow(char *str, unsigned int offset); static int find_cur_line(FIELD *cur, unsigned pos); static int split_line(FIELD *field, unsigned pos); static void bump_lines(FIELD *field, int pos, int amt, bool do_len); static bool check_field_size(FIELD *field); /* * Open the debug file if it is not already open.... */ #ifdef DEBUG int _formi_create_dbg_file(void) { if (dbg == NULL) { dbg = fopen("___form_dbg.out", "w"); if (dbg == NULL) { fprintf(stderr, "Cannot open debug file!\n"); return E_SYSTEM_ERROR; } } return E_OK; } #endif /* * Bump the lines array elements in the given field by the given amount. * The row to start acting on can either be inferred from the given position * or if the special value _FORMI_USE_CURRENT is set then the row will be * the row the cursor is currently on. */ static void bump_lines(FIELD *field, int pos, int amt, bool do_len) { int i, row; #ifdef DEBUG int dbg_ok = FALSE; #endif if (pos == _FORMI_USE_CURRENT) row = field->start_line + field->cursor_ypos; else row = find_cur_line(field, (unsigned) pos); #ifdef DEBUG if (_formi_create_dbg_file() == E_OK) { dbg_ok = TRUE; fprintf(dbg, "bump_lines: bump starting at row %d\n", row); fprintf(dbg, "bump_lines: len from %d to %d, end from %d to %d\n", field->lines[row].length, field->lines[row].length + amt, field->lines[row].end, field->lines[row].end + amt); } #endif if (((int)field->lines[row].length + amt) < 0) { field->lines[row].length = 0; field->lines[row].end = 0; } else { if (do_len == TRUE) field->lines[row].length += amt; } if (field->lines[row].length > 1) field->lines[row].end += amt; else field->lines[row].end = field->lines[row].start; for (i = row + 1; i < field->row_count; i++) { #ifdef DEBUG if (dbg_ok) { fprintf(dbg, "bump_lines: row %d: len from %d to %d, end from %d to %d\n", i, field->lines[i].start, field->lines[i].start + amt, field->lines[i].end, field->lines[i].end + amt); } fflush(dbg); #endif field->lines[i].start += amt; field->lines[i].end += amt; } } /* * Check the sizing of the field, if the maximum size is set for a * dynamic field then check that the number of rows or columns does * not exceed the set maximum. The decision to check the rows or * columns is made on the basis of how many rows are in the field - * one row means the max applies to the number of columns otherwise it * applies to the number of rows. If the row/column count is less * than the maximum then return TRUE. * */ static bool check_field_size(FIELD *field) { if ((field->opts & O_STATIC) != O_STATIC) { /* dynamic field */ if (field->max == 0) /* unlimited */ return TRUE; if (field->rows == 1) { return (field->buffers[0].length < field->max); } else { return (field->row_count <= field->max); } } else { if ((field->rows + field->nrows) == 1) { return (field->buffers[0].length < field->cols); } else { return (field->row_count <= (field->rows + field->nrows)); } } } /* * Set the form's current field to the first valid field on the page. * Assume the fields have been sorted and stitched. */ int _formi_pos_first_field(FORM *form) { FIELD *cur; int old_page; old_page = form->page; /* scan forward for an active page....*/ while (form->page_starts[form->page].in_use == 0) { form->page++; if (form->page > form->max_page) { form->page = old_page; return E_REQUEST_DENIED; } } /* then scan for a field we can use */ cur = form->fields[form->page_starts[form->page].first]; while ((cur->opts & (O_VISIBLE | O_ACTIVE)) != (O_VISIBLE | O_ACTIVE)) { cur = CIRCLEQ_NEXT(cur, glue); if (cur == (void *) &form->sorted_fields) { form->page = old_page; return E_REQUEST_DENIED; } } form->cur_field = cur->index; return E_OK; } /* * Set the field to the next active and visible field, the fields are * traversed in index order in the direction given. If the parameter * use_sorted is TRUE then the sorted field list will be traversed instead * of using the field index. */ int _formi_pos_new_field(FORM *form, unsigned direction, unsigned use_sorted) { FIELD *cur; int i; i = form->cur_field; cur = form->fields[i]; do { if (direction == _FORMI_FORWARD) { if (use_sorted == TRUE) { if ((form->wrap == FALSE) && (cur == CIRCLEQ_LAST(&form->sorted_fields))) return E_REQUEST_DENIED; cur = CIRCLEQ_NEXT(cur, glue); i = cur->index; } else { if ((form->wrap == FALSE) && ((i + 1) >= form->field_count)) return E_REQUEST_DENIED; i++; if (i >= form->field_count) i = 0; } } else { if (use_sorted == TRUE) { if ((form->wrap == FALSE) && (cur == CIRCLEQ_FIRST(&form->sorted_fields))) return E_REQUEST_DENIED; cur = CIRCLEQ_PREV(cur, glue); i = cur->index; } else { if ((form->wrap == FALSE) && (i <= 0)) return E_REQUEST_DENIED; i--; if (i < 0) i = form->field_count - 1; } } if ((form->fields[i]->opts & (O_VISIBLE | O_ACTIVE)) == (O_VISIBLE | O_ACTIVE)) { form->cur_field = i; return E_OK; } } while (i != form->cur_field); return E_REQUEST_DENIED; } /* * Find the line in a field that the cursor is currently on. */ static int find_cur_line(FIELD *cur, unsigned pos) { unsigned row; /* first check if pos is at the end of the string, if this * is true then just return the last row since the pos may * not have been added to the lines array yet. */ if (pos == (cur->buffers[0].length - 1)) return (cur->row_count - 1); for (row = 0; row < cur->row_count; row++) { if ((pos >= cur->lines[row].start) && (pos <= cur->lines[row].end)) return row; } #ifdef DEBUG /* barf if we get here, this should not be possible */ assert((row != row)); #endif return 0; } /* * Word wrap the contents of the field's buffer 0 if this is allowed. * If the wrap is successful, that is, the row count nor the buffer * size is exceeded then the function will return E_OK, otherwise it * will return E_REQUEST_DENIED. */ int _formi_wrap_field(FIELD *field, unsigned int loc) { char *str; int width, row, start_row; unsigned int pos; str = field->buffers[0].string; /* Don't bother if the field string is too short. */ if (field->buffers[0].length < field->cols) return E_OK; if ((field->opts & O_STATIC) == O_STATIC) { if ((field->rows + field->nrows) == 1) { return E_OK; /* cannot wrap a single line */ } width = field->cols; } else { /* if we are limited to one line then don't try to wrap */ if ((field->drows + field->nrows) == 1) { return E_OK; } /* * hueristic - if a dynamic field has more than one line * on the screen then the field grows rows, otherwise * it grows columns, effectively a single line field. * This is documented AT&T behaviour. */ if (field->rows > 1) { width = field->cols; } else { return E_OK; } } start_row = find_cur_line(field, loc); /* if we are not at the top of the field then back up one * row because we may be able to merge the current row into * the one above. */ if (start_row > 0) start_row--; for (row = start_row; row < field->row_count; row++) { AGAIN: pos = field->lines[row].end; if (field->lines[row].length < width) { /* line may be too short, try joining some lines */ if ((((int) field->row_count) - 1) == row) { /* if this is the last row then don't * wrap */ continue; } if (_formi_join_line(field, (unsigned int) pos, JOIN_NEXT_NW) == E_OK) { goto AGAIN; } else break; } else { /* line is too long, split it - maybe */ /* first check if we have not run out of room */ if ((field->opts & O_STATIC) == O_STATIC) { /* check static field */ if ((field->rows + field->nrows - 1) == row) return E_REQUEST_DENIED; } else { /* check dynamic field */ if ((field->max != 0) && ((field->max - 1) == row)) return E_REQUEST_DENIED; } /* split on first whitespace before current word */ pos = width + field->lines[row].start - 1; if (pos >= field->buffers[0].length) pos = field->buffers[0].length - 1; if ((!isblank(str[pos])) && ((field->opts & O_WRAP) == O_WRAP)) { if (!isblank(str[pos - 1])) pos = find_sow(str, (unsigned int) pos); /* * If we cannot split the line then return * NO_ROOM so the driver can tell that it * should not autoskip (if that is enabled) */ if ((pos == 0) || (!isblank(str[pos - 1])) || ((pos <= field->lines[row].start) && (field->buffers[0].length >= (width - 1 + field->lines[row].start)))) { return E_NO_ROOM; } } /* if we are at the end of the string and it has * a trailing blank, don't wrap the blank. */ if ((pos == field->buffers[0].length - 1) && (isblank(str[pos]))) continue; /* * otherwise, if we are still sitting on a * blank but not at the end of the line * move forward one char so the blank * is on the line boundary. */ if (isblank(str[pos])) pos++; if (split_line(field, pos) != E_OK) { return E_REQUEST_DENIED; } } } return E_OK; } /* * Join the two lines that surround the location pos, the type * variable indicates the direction of the join, JOIN_NEXT will join * the next line to the current line, JOIN_PREV will join the current * line to the previous line, the new lines will be wrapped unless the * _NW versions of the directions are used. Returns E_OK if the join * was successful or E_REQUEST_DENIED if the join cannot happen. */ static int _formi_join_line(FIELD *field, unsigned int pos, int direction) { unsigned int row, i; int old_alloced, old_row_count; struct _formi_field_lines *saved; #ifdef DEBUG int dbg_ok = FALSE; if (_formi_create_dbg_file() == E_OK) { dbg_ok = TRUE; } #endif if ((saved = (struct _formi_field_lines *) malloc(field->lines_alloced * sizeof(struct _formi_field_lines))) == NULL) return E_REQUEST_DENIED; bcopy(field->lines, saved, field->row_count * sizeof(struct _formi_field_lines)); old_alloced = field->lines_alloced; old_row_count = field->row_count; row = find_cur_line(field, pos); #ifdef DEBUG if (dbg_ok == TRUE) { fprintf(dbg, "join_line: working on row %d, row_count = %d\n", row, field->row_count); } #endif if ((direction == JOIN_NEXT) || (direction == JOIN_NEXT_NW)) { /* see if there is another line following... */ if (row == (field->row_count - 1)) { free(saved); return E_REQUEST_DENIED; } #ifdef DEBUG if (dbg_ok == TRUE) { fprintf(dbg, "join_line: join_next before end = %d, length = %d", field->lines[row].end, field->lines[row].length); fprintf(dbg, " :: next row end = %d, length = %d\n", field->lines[row + 1].end, field->lines[row + 1].length); } #endif field->lines[row].end = field->lines[row + 1].end; field->lines[row].length += field->lines[row + 1].length; /* shift all the remaining lines up.... */ for (i = row + 2; i < field->row_count; i++) field->lines[i - 1] = field->lines[i]; } else { if ((pos == 0) || (row == 0)) { free(saved); return E_REQUEST_DENIED; } #ifdef DEBUG if (dbg_ok == TRUE) { fprintf(dbg, "join_line: join_prev before end = %d, length = %d", field->lines[row].end, field->lines[row].length); fprintf(dbg, " :: prev row end = %d, length = %d\n", field->lines[row - 1].end, field->lines[row - 1].length); } #endif field->lines[row - 1].end = field->lines[row].end; field->lines[row - 1].length += field->lines[row].length; /* shift all the remaining lines up */ for (i = row + 1; i < field->row_count; i++) field->lines[i - 1] = field->lines[i]; } #ifdef DEBUG if (dbg_ok == TRUE) { fprintf(dbg, "join_line: exit end = %d, length = %d\n", field->lines[row].end, field->lines[row].length); } #endif field->row_count--; /* wrap the field if required, if this fails undo the change */ if ((direction == JOIN_NEXT) || (direction == JOIN_PREV)) { if (_formi_wrap_field(field, (unsigned int) pos) != E_OK) { free(field->lines); field->lines = saved; field->lines_alloced = old_alloced; field->row_count = old_row_count; return E_REQUEST_DENIED; } } free(saved); return E_OK; } /* * Split the line at the given position, if possible */ static int split_line(FIELD *field, unsigned pos) { struct _formi_field_lines *new_lines; unsigned int row, i; #ifdef DEBUG short dbg_ok = FALSE; #endif if (pos == 0) return E_REQUEST_DENIED; #ifdef DEBUG if (_formi_create_dbg_file() == E_OK) { fprintf(dbg, "split_line: splitting line at %d\n", pos); dbg_ok = TRUE; } #endif if ((field->row_count + 1) > field->lines_alloced) { if ((new_lines = (struct _formi_field_lines *) realloc(field->lines, (field->row_count + 1) * sizeof(struct _formi_field_lines))) == NULL) return E_SYSTEM_ERROR; field->lines = new_lines; field->lines_alloced++; } row = find_cur_line(field, pos); #ifdef DEBUG if (dbg_ok == TRUE) { fprintf(dbg, "split_line: enter: lines[%d].end = %d, lines[%d].length = %d\n", row, field->lines[row].end, row, field->lines[row].length); } assert(((field->lines[row].end < INT_MAX) && (field->lines[row].length < INT_MAX) && (field->lines[row].length > 0))); #endif /* if asked to split right where the line already starts then * just return - nothing to do. */ if (field->lines[row].start == pos) return E_OK; for (i = field->row_count - 1; i > row; i--) { field->lines[i + 1] = field->lines[i]; } field->lines[row + 1].end = field->lines[row].end; field->lines[row].end = pos - 1; field->lines[row].length = pos - field->lines[row].start; field->lines[row + 1].start = pos; field->lines[row + 1].length = field->lines[row + 1].end - field->lines[row + 1].start + 1; #ifdef DEBUG assert(((field->lines[row + 1].end < INT_MAX) && (field->lines[row].end < INT_MAX) && (field->lines[row].length < INT_MAX) && (field->lines[row + 1].start < INT_MAX) && (field->lines[row + 1].length < INT_MAX) && (field->lines[row].length > 0) && (field->lines[row + 1].length > 0))); if (dbg_ok == TRUE) { fprintf(dbg, "split_line: exit: lines[%d].end = %d, lines[%d].length = %d, ", row, field->lines[row].end, row, field->lines[row].length); fprintf(dbg, "lines[%d].start = %d, lines[%d].end = %d, ", row + 1, field->lines[row + 1].start, row + 1, field->lines[row + 1].end); fprintf(dbg, "lines[%d].length = %d, row_count = %d\n", row + 1, field->lines[row + 1].length, field->row_count + 1); } #endif field->row_count++; #ifdef DEBUG if (dbg_ok == TRUE) { bump_lines(field, 0, 0, FALSE); /* will report line data for us */ } #endif return E_OK; } /* * skip the blanks in the given string, start at the index start and * continue forward until either the end of the string or a non-blank * character is found. Return the index of either the end of the string or * the first non-blank character. */ unsigned _formi_skip_blanks(char *string, unsigned int start) { unsigned int i; i = start; while ((string[i] != '\0') && isblank(string[i])) i++; return i; } /* * Return the index of the top left most field of the two given fields. */ static int _formi_top_left(FORM *form, int a, int b) { /* lower row numbers always win here.... */ if (form->fields[a]->form_row < form->fields[b]->form_row) return a; if (form->fields[a]->form_row > form->fields[b]->form_row) return b; /* rows must be equal, check columns */ if (form->fields[a]->form_col < form->fields[b]->form_col) return a; if (form->fields[a]->form_col > form->fields[b]->form_col) return b; /* if we get here fields must be in exactly the same place, punt */ return a; } /* * Return the index to the field that is the bottom-right-most of the * two given fields. */ static int _formi_bottom_right(FORM *form, int a, int b) { /* check the rows first, biggest row wins */ if (form->fields[a]->form_row > form->fields[b]->form_row) return a; if (form->fields[a]->form_row < form->fields[b]->form_row) return b; /* rows must be equal, check cols, biggest wins */ if (form->fields[a]->form_col > form->fields[b]->form_col) return a; if (form->fields[a]->form_col < form->fields[b]->form_col) return b; /* fields in the same place, punt */ return a; } /* * Find the end of the current word in the string str, starting at * offset - the end includes any trailing whitespace. If the end of * the string is found before a new word then just return the offset * to the end of the string. */ static int find_eow(char *str, unsigned int offset) { int start; start = offset; /* first skip any non-whitespace */ while ((str[start] != '\0') && !isblank(str[start])) start++; /* see if we hit the end of the string */ if (str[start] == '\0') return start; /* otherwise skip the whitespace.... */ while ((str[start] != '\0') && isblank(str[start])) start++; return start; } /* * Find the beginning of the current word in the string str, starting * at offset. */ static int find_sow(char *str, unsigned int offset) { int start; start = offset; if (start > 0) { if (isblank(str[start]) || isblank(str[start - 1])) { if (isblank(str[start - 1])) start--; /* skip the whitespace.... */ while ((start >= 0) && isblank(str[start])) start--; } } /* see if we hit the start of the string */ if (start < 0) return 0; /* now skip any non-whitespace */ while ((start >= 0) && !isblank(str[start])) start--; if (start > 0) start++; /* last loop has us pointing at a space, adjust */ if (start < 0) start = 0; return start; } /* * Scroll the field forward the given number of lines. */ static void _formi_scroll_fwd(FIELD *field, unsigned int amt) { /* check if we have lines to scroll */ if (field->row_count < (field->start_line + field->rows)) return; field->start_line += min(amt, field->row_count - field->start_line - field->rows); } /* * Scroll the field backward the given number of lines. */ static void _formi_scroll_back(FIELD *field, unsigned int amt) { if (field->start_line == 0) return; field->start_line -= min(field->start_line, amt); } /* * Scroll the field forward the given number of characters. */ void _formi_hscroll_fwd(FIELD *field, int unsigned amt) { field->start_char += min(amt, field->lines[field->start_line + field->cursor_ypos].end); } /* * Scroll the field backward the given number of characters. */ void _formi_hscroll_back(FIELD *field, unsigned int amt) { field->start_char -= min(field->start_char, amt); } /* * Find the different pages in the form fields and assign the form * page_starts array with the information to find them. */ int _formi_find_pages(FORM *form) { int i, cur_page = 0; if ((form->page_starts = (_FORMI_PAGE_START *) malloc((form->max_page + 1) * sizeof(_FORMI_PAGE_START))) == NULL) return E_SYSTEM_ERROR; /* initialise the page starts array */ memset(form->page_starts, 0, (form->max_page + 1) * sizeof(_FORMI_PAGE_START)); for (i =0; i < form->field_count; i++) { if (form->fields[i]->page_break == 1) cur_page++; if (form->page_starts[cur_page].in_use == 0) { form->page_starts[cur_page].in_use = 1; form->page_starts[cur_page].first = i; form->page_starts[cur_page].last = i; form->page_starts[cur_page].top_left = i; form->page_starts[cur_page].bottom_right = i; } else { form->page_starts[cur_page].last = i; form->page_starts[cur_page].top_left = _formi_top_left(form, form->page_starts[cur_page].top_left, i); form->page_starts[cur_page].bottom_right = _formi_bottom_right(form, form->page_starts[cur_page].bottom_right, i); } } return E_OK; } /* * Completely redraw the field of the given form. */ void _formi_redraw_field(FORM *form, int field) { unsigned int pre, post, flen, slen, i, row, start, last_row; char *str; FIELD *cur; #ifdef DEBUG char buffer[100]; #endif cur = form->fields[field]; str = cur->buffers[0].string; flen = cur->cols; slen = 0; start = 0; if ((cur->row_count - cur->start_line) < cur->rows) last_row = cur->row_count; else last_row = cur->start_line + cur->rows; for (row = cur->start_line; row < last_row; row++) { wmove(form->scrwin, (int) (cur->form_row + row - cur->start_line), (int) cur->form_col); start = cur->lines[row].start; slen = cur->lines[row].length; if ((cur->opts & O_STATIC) == O_STATIC) { switch (cur->justification) { case JUSTIFY_RIGHT: post = 0; if (flen < slen) pre = 0; else pre = flen - slen; break; case JUSTIFY_CENTER: if (flen < slen) { pre = 0; post = 0; } else { pre = flen - slen; post = pre = pre / 2; /* get padding right if centring is not even */ if ((post + pre + slen) < flen) post++; } break; case NO_JUSTIFICATION: case JUSTIFY_LEFT: default: pre = 0; if (flen <= slen) post = 0; else { post = flen - slen; if (post > flen) post = flen; } break; } } else { /* dynamic fields are not justified */ pre = 0; if (flen <= slen) post = 0; else { post = flen - slen; if (post > flen) post = flen; } /* but they do scroll.... */ if (pre > cur->start_char - start) pre = pre - cur->start_char + start; else pre = 0; if (slen > cur->start_char) { slen -= cur->start_char; if (slen > flen) post = 0; else post = flen - slen; if (post > flen) post = flen; } else { slen = 0; post = flen - pre; } } if (form->cur_field == field) wattrset(form->scrwin, cur->fore); else wattrset(form->scrwin, cur->back); #ifdef DEBUG if (_formi_create_dbg_file() == E_OK) { fprintf(dbg, "redraw_field: start=%d, pre=%d, slen=%d, flen=%d, post=%d, start_char=%d\n", start, pre, slen, flen, post, cur->start_char); if (str != NULL) { strncpy(buffer, &str[cur->start_char + cur->lines[row].start], flen); } else { strcpy(buffer, "(null)"); } buffer[flen] = '\0'; fprintf(dbg, "redraw_field: %s\n", buffer); } #endif for (i = start + cur->start_char; i < pre; i++) waddch(form->scrwin, cur->pad); #ifdef DEBUG fprintf(dbg, "redraw_field: will add %d chars\n", min(slen, flen)); #endif for (i = 0; i < min(slen, flen); i++) { #ifdef DEBUG fprintf(dbg, "adding char str[%d]=%c\n", i + cur->start_char + cur->lines[row].start, str[i + cur->start_char + cur->lines[row].start]); #endif if (((cur->opts & O_PUBLIC) != O_PUBLIC)) { waddch(form->scrwin, cur->pad); } else if ((cur->opts & O_VISIBLE) == O_VISIBLE) { waddch(form->scrwin, str[i + cur->start_char + cur->lines[row].start]); } else { waddch(form->scrwin, ' '); } } for (i = 0; i < post; i++) waddch(form->scrwin, cur->pad); } for (row = cur->row_count - cur->start_line; row < cur->rows; row++) { wmove(form->scrwin, (int) (cur->form_row + row), (int) cur->form_col); for (i = 0; i < cur->cols; i++) { waddch(form->scrwin, cur->pad); } } return; } /* * Display the fields attached to the form that are on the current page * on the screen. * */ int _formi_draw_page(FORM *form) { int i; if (form->page_starts[form->page].in_use == 0) return E_BAD_ARGUMENT; wclear(form->scrwin); for (i = form->page_starts[form->page].first; i <= form->page_starts[form->page].last; i++) _formi_redraw_field(form, i); return E_OK; } /* * Add the character c at the position pos in buffer 0 of the given field */ int _formi_add_char(FIELD *field, unsigned int pos, char c) { char *new, old_c; unsigned int new_size; int status; /* * If buffer has not had a string before, set it to a blank * string. Everything should flow from there.... */ if (field->buffers[0].string == NULL) { set_field_buffer(field, 0, ""); } if (_formi_validate_char(field, c) != E_OK) { #ifdef DEBUG fprintf(dbg, "add_char: char %c failed char validation\n", c); #endif return E_INVALID_FIELD; } #ifdef DEBUG fprintf(dbg, "add_char: pos=%d, char=%c\n", pos, c); fprintf(dbg, "add_char enter: xpos=%d, start=%d, length=%d(%d), allocated=%d\n", field->cursor_xpos, field->start_char, field->buffers[0].length, strlen(field->buffers[0].string), field->buffers[0].allocated); fprintf(dbg, "add_char enter: %s\n", field->buffers[0].string); fprintf(dbg, "add_char enter: buf0_status=%d\n", field->buf0_status); #endif if (((field->opts & O_BLANK) == O_BLANK) && (field->buf0_status == FALSE) && ((field->cursor_xpos + field->start_char) == 0)) { field->buffers[0].length = 0; field->buffers[0].string[0] = '\0'; pos = 0; field->start_char = 0; field->start_line = 0; field->row_count = 1; field->cursor_xpos = 0; field->cursor_ypos = 0; field->lines[0].start = 0; field->lines[0].end = 0; field->lines[0].length = 0; } if ((field->overlay == 0) || ((field->overlay == 1) && (pos >= field->buffers[0].length))) { /* first check if the field can have more chars...*/ if (check_field_size(field) == FALSE) return E_REQUEST_DENIED; if (field->buffers[0].length + 1 >= field->buffers[0].allocated) { new_size = field->buffers[0].allocated + 64 - (field->buffers[0].allocated % 64); if ((new = (char *) realloc(field->buffers[0].string, new_size )) == NULL) return E_SYSTEM_ERROR; field->buffers[0].allocated = new_size; field->buffers[0].string = new; } } if ((field->overlay == 0) && (field->buffers[0].length > pos)) { bcopy(&field->buffers[0].string[pos], &field->buffers[0].string[pos + 1], field->buffers[0].length - pos + 1); } old_c = field->buffers[0].string[pos]; field->buffers[0].string[pos] = c; if (pos >= field->buffers[0].length) { /* make sure the string is terminated if we are at the * end of the string, the terminator would be missing * if we are are at the end of the field. */ field->buffers[0].string[pos + 1] = '\0'; } /* only increment the length if we are inserting characters * OR if we are at the end of the field in overlay mode. */ if ((field->overlay == 0) || ((field->overlay == 1) && (pos >= field->buffers[0].length))) { field->buffers[0].length++; bump_lines(field, (int) pos, 1, TRUE); } /* wrap the field, if needed */ status = _formi_wrap_field(field, pos); /* * check the wrap worked or that we have not exceeded the * max field size - this can happen if the field is re-wrapped * and the row count is increased past the set limit. */ if ((status != E_OK) || (check_field_size(field) == FALSE)) { if ((field->overlay == 0) || ((field->overlay == 1) && (pos >= field->buffers[0].length))) { /* * wrap failed for some reason, back out the * char insert */ bcopy(&field->buffers[0].string[pos + 1], &field->buffers[0].string[pos], field->buffers[0].length - pos); field->buffers[0].length--; bump_lines(field, (int) pos, -1, TRUE); } else if (field->overlay == 1) { /* back out character overlay */ field->buffers[0].string[pos] = old_c; } _formi_wrap_field(field, ((pos > 0)? pos - 1 : 0)); } else { field->buf0_status = TRUE; if ((field->rows + field->nrows) == 1) { if ((field->cursor_xpos < (field->cols - 1)) || ((field->opts & O_STATIC) != O_STATIC)) field->cursor_xpos++; if (field->cursor_xpos > field->cols) { field->start_char++; field->cursor_xpos = field->cols; } } else { new_size = find_cur_line(field, pos); if (new_size >= field->rows) { field->cursor_ypos = field->rows - 1; field->start_line = field->row_count - field->cursor_ypos - 1; } else field->cursor_ypos = new_size; if ((field->lines[new_size].start) <= (pos + 1)) { field->cursor_xpos = pos - field->lines[new_size].start + 1; } else { field->cursor_xpos = 0; } /* * Annoying corner case - if we are right in * the bottom right corner of the field we * need to scroll the field one line so the * cursor is positioned correctly in the * field. */ if ((field->cursor_xpos >= field->cols) && (field->cursor_ypos == (field->rows - 1))) { field->cursor_ypos--; field->start_line++; } } } #ifdef DEBUG assert((field->cursor_xpos <= field->cols) && (field->cursor_ypos < 400000) && (field->start_line < 400000)); fprintf(dbg, "add_char exit: xpos=%d, start=%d, length=%d(%d), allocated=%d\n", field->cursor_xpos, field->start_char, field->buffers[0].length, strlen(field->buffers[0].string), field->buffers[0].allocated); fprintf(dbg, "add_char exit: ypos=%d, start_line=%d\n", field->cursor_ypos, field->start_line); fprintf(dbg,"add_char exit: %s\n", field->buffers[0].string); fprintf(dbg, "add_char exit: buf0_status=%d\n", field->buf0_status); fprintf(dbg, "add_char exit: status = %s\n", (status == E_OK)? "OK" : "FAILED"); #endif return status; } /* * Manipulate the text in a field, this takes the given form and performs * the passed driver command on the current text field. Returns 1 if the * text field was modified. */ int _formi_manipulate_field(FORM *form, int c) { FIELD *cur; char *str, saved; unsigned int i, start, end, pos, row, status, old_count; int len; cur = form->fields[form->cur_field]; #ifdef DEBUG fprintf(dbg, "entry: xpos=%d, start_char=%d, length=%d, allocated=%d\n", cur->cursor_xpos, cur->start_char, cur->buffers[0].length, cur->buffers[0].allocated); fprintf(dbg, "entry: start_line=%d, ypos=%d\n", cur->start_line, cur->cursor_ypos); fprintf(dbg, "entry: string="); if (cur->buffers[0].string == NULL) fprintf(dbg, "(null)\n"); else fprintf(dbg, "\"%s\"\n", cur->buffers[0].string); #endif /* Cannot manipulate a null string! */ if (cur->buffers[0].string == NULL) return E_REQUEST_DENIED; switch (c) { case REQ_NEXT_CHAR: /* for a dynamic field allow an offset of one more * char so we can insert chars after end of string. * Static fields cannot do this so deny request if * cursor is at the end of the field. */ if (((cur->opts & O_STATIC) == O_STATIC) && (cur->cursor_xpos == cur->cols - 1) && ((cur->rows + cur->nrows) == 1)) return E_REQUEST_DENIED; if ((cur->cursor_xpos + cur->start_char + 1) > cur->buffers[0].length) return E_REQUEST_DENIED; if ((cur->rows + cur->nrows) == 1) { cur->cursor_xpos++; if (cur->cursor_xpos >= cur->cols - 1) { cur->cursor_xpos = cur->cols - 1; if ((cur->opts & O_STATIC) != O_STATIC) cur->start_char++; } } else { row = cur->start_line + cur->cursor_ypos; if (cur->cursor_xpos == (cur->lines[row].length - 1)) { if ((row + 1) >= cur->row_count) return E_REQUEST_DENIED; cur->cursor_xpos = 0; if (cur->cursor_ypos == (cur->rows - 1)) cur->start_line++; else cur->cursor_ypos++; } else cur->cursor_xpos++; } break; case REQ_PREV_CHAR: if ((cur->rows + cur->nrows) == 1) { if (cur->cursor_xpos == 0) { if (cur->start_char > 0) cur->start_char--; else return E_REQUEST_DENIED; } else cur->cursor_xpos--; } else { if ((cur->cursor_xpos == 0) && (cur->cursor_ypos == 0) && (cur->start_line == 0)) return E_REQUEST_DENIED; if (cur->cursor_xpos > 0) { cur->cursor_xpos--; } else { if (cur->cursor_ypos > 0) cur->cursor_ypos--; else cur->start_line--; cur->cursor_xpos = cur->lines[cur->start_line + cur->cursor_ypos].length - 1; } } break; case REQ_NEXT_LINE: if ((cur->start_line + cur->cursor_ypos + 1) >= cur->row_count) return E_REQUEST_DENIED; if ((cur->cursor_ypos + 1) >= cur->rows) { cur->start_line++; } else cur->cursor_ypos++; break; case REQ_PREV_LINE: if (cur->cursor_ypos == 0) { if (cur->start_line == 0) return E_REQUEST_DENIED; cur->start_line--; } else cur->cursor_ypos--; break; case REQ_NEXT_WORD: start = cur->lines[cur->start_line + cur->cursor_ypos].start + cur->cursor_xpos + cur->start_char; str = cur->buffers[0].string; start = find_eow(str, start); /* check if we hit the end */ if (str[start] == '\0') return E_REQUEST_DENIED; /* otherwise we must have found the start of a word...*/ if ((cur->rows + cur->nrows) == 1) { /* single line field */ if (start - cur->start_char < cur->cols) { cur->cursor_xpos = start; } else { cur->start_char = start; cur->cursor_xpos = 0; } } else { /* multiline field */ row = find_cur_line(cur, start); cur->cursor_xpos = start - cur->lines[row].start; if (row != (cur->start_line + cur->cursor_ypos)) { if (cur->cursor_ypos == (cur->rows - 1)) { cur->start_line = row - cur->rows + 1; } else { cur->cursor_ypos = row - cur->start_line; } } } break; case REQ_PREV_WORD: start = cur->start_char + cur->cursor_xpos + cur->lines[cur->start_line + cur->cursor_ypos].start; if (cur->start_char > 0) start--; if (start == 0) return E_REQUEST_DENIED; str = cur->buffers[0].string; start = find_sow(str, start); if ((cur->rows + cur->nrows) == 1) { /* single line field */ if (start - cur->start_char > 0) { cur->cursor_xpos = start; } else { cur->start_char = start; cur->cursor_xpos = 0; } } else { /* multiline field */ row = find_cur_line(cur, start); cur->cursor_xpos = start - cur->lines[row].start; if (row != (cur->start_line + cur->cursor_ypos)) { if (cur->cursor_ypos == 0) { cur->start_line = row; } else { if (cur->start_line > row) { cur->start_line = row; cur->cursor_ypos = 0; } else { cur->cursor_ypos = row - cur->start_line; } } } } break; case REQ_BEG_FIELD: cur->start_char = 0; cur->start_line = 0; cur->cursor_xpos = 0; cur->cursor_ypos = 0; break; case REQ_BEG_LINE: cur->cursor_xpos = 0; cur->start_char = 0; break; case REQ_END_FIELD: if (cur->row_count > cur->rows) { cur->start_line = cur->row_count - cur->rows; cur->cursor_ypos = cur->rows - 1; } else { cur->start_line = 0; cur->cursor_ypos = cur->row_count - 1; } /* we fall through here deliberately, we are on the * correct row, now we need to get to the end of the * line. */ /* FALLTHRU */ case REQ_END_LINE: start = cur->lines[cur->start_line + cur->cursor_ypos].start; end = cur->lines[cur->start_line + cur->cursor_ypos].end; if ((cur->rows + cur->nrows) == 1) { if (end - start > cur->cols - 1) { cur->cursor_xpos = cur->cols - 1; cur->start_char = end - cur->cols; if ((cur->opts & O_STATIC) != O_STATIC) cur->start_char++; } else { cur->cursor_xpos = end - start + 1; if (((cur->opts & O_STATIC) == O_STATIC) && ((end - start) == (cur->cols - 1))) cur->cursor_xpos--; cur->start_char = start; } } else { cur->cursor_xpos = end - start + 1; } break; case REQ_LEFT_CHAR: if ((cur->cursor_xpos == 0) && (cur->start_char == 0) && (cur->start_line == 0) && (cur->cursor_ypos == 0)) return E_REQUEST_DENIED; if (cur->cursor_xpos == 0) { if ((cur->rows + cur->nrows) == 1) { if (cur->start_char > 0) cur->start_char--; else return E_REQUEST_DENIED; } else { if ((cur->cursor_ypos == 0) && (cur->start_line == 0)) return E_REQUEST_DENIED; if (cur->cursor_ypos == 0) cur->start_line--; else cur->cursor_ypos--; cur->cursor_xpos = cur->lines[cur->cursor_ypos + cur->start_line].length; } } else cur->cursor_xpos--; break; case REQ_RIGHT_CHAR: pos = cur->start_char + cur->cursor_xpos; row = cur->start_line + cur->cursor_ypos; end = cur->lines[row].end; if (cur->buffers[0].string[pos] == '\0') return E_REQUEST_DENIED; #ifdef DEBUG fprintf(dbg, "req_right_char enter: start=%d, xpos=%d, c=%c\n", cur->start_char, cur->cursor_xpos, cur->buffers[0].string[pos]); #endif if (pos == end) { start = pos + 1; if ((cur->buffers[0].length <= start) || ((row + 1) >= cur->row_count)) return E_REQUEST_DENIED; if ((cur->cursor_ypos + 1) >= cur->rows) { cur->start_line++; cur->cursor_ypos = cur->rows - 1; } else cur->cursor_ypos++; cur->cursor_xpos = 0; } else { if (((cur->rows + cur->nrows) == 1) && (cur->cursor_xpos == cur->cols - 1)) cur->start_char++; else cur->cursor_xpos++; } #ifdef DEBUG fprintf(dbg, "req_right_char exit: start=%d, xpos=%d, c=%c\n", cur->start_char, cur->cursor_xpos, cur->buffers[0].string[cur->start_char + cur->cursor_xpos]); #endif break; case REQ_UP_CHAR: if (cur->cursor_ypos == 0) { if (cur->start_line == 0) return E_REQUEST_DENIED; cur->start_line--; } else cur->cursor_ypos--; row = cur->start_line + cur->cursor_ypos; if (cur->cursor_xpos > cur->lines[row].length) cur->cursor_xpos = cur->lines[row].length; break; case REQ_DOWN_CHAR: if (cur->cursor_ypos == cur->rows - 1) { if (cur->start_line + cur->rows == cur->row_count) return E_REQUEST_DENIED; cur->start_line++; } else cur->cursor_ypos++; row = cur->start_line + cur->cursor_ypos; if (cur->cursor_xpos > cur->lines[row].length) cur->cursor_xpos = cur->lines[row].length; break; case REQ_NEW_LINE: if ((status = split_line(cur, cur->start_char + cur->cursor_xpos)) != E_OK) return status; break; case REQ_INS_CHAR: if ((status = _formi_add_char(cur, cur->start_char + cur->cursor_xpos, cur->pad)) != E_OK) return status; break; case REQ_INS_LINE: start = cur->lines[cur->start_line + cur->cursor_ypos].start; if ((status = split_line(cur, start)) != E_OK) return status; break; case REQ_DEL_CHAR: if (cur->buffers[0].length == 0) return E_REQUEST_DENIED; row = cur->start_line + cur->cursor_ypos; start = cur->start_char + cur->cursor_xpos + cur->lines[row].start; end = cur->buffers[0].length; if (start == cur->lines[row].end) { if ((cur->rows + cur->nrows) > 1) { if (cur->row_count > 1) { if (_formi_join_line(cur, start, JOIN_NEXT_NW) != E_OK) { return E_REQUEST_DENIED; } } else return E_REQUEST_DENIED; } else return E_REQUEST_DENIED; } saved = cur->buffers[0].string[start]; bcopy(&cur->buffers[0].string[start + 1], &cur->buffers[0].string[start], (unsigned) end - start + 1); cur->buffers[0].length--; bump_lines(cur, _FORMI_USE_CURRENT, -1, TRUE); if ((cur->rows + cur->nrows) > 1) { if (_formi_wrap_field(cur, start) != E_OK) { bcopy(&cur->buffers[0].string[start], &cur->buffers[0].string[start + 1], (unsigned) end - start); cur->buffers[0].length++; cur->buffers[0].string[start] = saved; bump_lines(cur, _FORMI_USE_CURRENT, 1, TRUE); _formi_wrap_field(cur, start); return E_REQUEST_DENIED; } } break; case REQ_DEL_PREV: if ((cur->cursor_xpos == 0) && (cur->start_char == 0) && (cur->start_line == 0) && (cur->cursor_ypos == 0)) return E_REQUEST_DENIED; row = cur->start_line + cur->cursor_ypos; start = cur->cursor_xpos + cur->start_char + cur->lines[row].start; end = cur->buffers[0].length; if ((cur->start_char + cur->cursor_xpos) == 0) { if (_formi_join_line(cur, cur->lines[row].start, JOIN_PREV_NW) != E_OK) { return E_REQUEST_DENIED; } } saved = cur->buffers[0].string[start - 1]; bcopy(&cur->buffers[0].string[start], &cur->buffers[0].string[start - 1], (unsigned) end - start + 1); bump_lines(cur, (int) start - 1, -1, TRUE); cur->buffers[0].length--; if ((cur->rows + cur->nrows) == 1) { if ((cur->cursor_xpos == 0) && (cur->start_char > 0)) cur->start_char--; else if ((cur->cursor_xpos == cur->cols - 1) && (cur->start_char > 0)) cur->start_char--; else if (cur->cursor_xpos > 0) cur->cursor_xpos--; } else { pos = start - 1; if (pos >= cur->buffers[0].length) pos = cur->buffers[0].length - 1; if ((_formi_wrap_field(cur, pos) != E_OK)) { bcopy(&cur->buffers[0].string[start - 1], &cur->buffers[0].string[start], (unsigned) end - start); cur->buffers[0].length++; cur->buffers[0].string[start - 1] = saved; bump_lines(cur, (int) start - 1, 1, TRUE); _formi_wrap_field(cur, pos); return E_REQUEST_DENIED; } row = find_cur_line(cur, pos); cur->cursor_xpos = start - cur->lines[row].start - 1; if (row >= cur->rows) cur->start_line = row - cur->cursor_ypos; else { cur->start_line = 0; cur->cursor_ypos = row; } } break; case REQ_DEL_LINE: row = cur->start_line + cur->cursor_ypos; start = cur->lines[row].start; end = cur->lines[row].end; bcopy(&cur->buffers[0].string[end + 1], &cur->buffers[0].string[start], (unsigned) cur->buffers[0].length - end + 1); if (((cur->rows + cur->nrows) == 1) || (cur->row_count == 1)) { /* single line case */ cur->buffers[0].length = 0; cur->lines[0].end = cur->lines[0].length = 0; cur->cursor_xpos = cur->cursor_ypos = 0; } else { /* multiline field */ old_count = cur->row_count; cur->row_count--; if (cur->row_count == 0) cur->row_count = 1; if (cur->row_count > 1) bcopy(&cur->lines[row + 1], &cur->lines[row], (unsigned) (cur->row_count - row) * sizeof(struct _formi_field_lines)); cur->lines[row].start = start; len = start - end - 1; /* yes, this is negative */ if (row < (cur->row_count - 1)) bump_lines(cur, (int) start, len, FALSE); else if (old_count == 1) { cur->lines[0].end = cur->lines[0].length = 0; cur->cursor_xpos = 0; cur->cursor_ypos = 0; } else if (cur->row_count == 1) { cur->lines[0].length = cur->buffers[0].length + len; cur->lines[0].end = cur->lines[0].length - 1; } cur->buffers[0].length += len; if (row > (cur->row_count - 1)) { row--; if (cur->cursor_ypos == 0) { if (cur->start_line > 0) { cur->start_line--; } } else { cur->cursor_ypos--; } } if (old_count > 1) { if (cur->cursor_xpos > cur->lines[row].length) cur->cursor_xpos = cur->lines[row].length - 1; if (row >= cur->rows) cur->start_line = row - cur->cursor_ypos; else { cur->start_line = 0; cur->cursor_ypos = row; } } } break; case REQ_DEL_WORD: start = cur->start_char + cur->cursor_xpos; end = find_eow(cur->buffers[0].string, start); start = find_sow(cur->buffers[0].string, start); bcopy(&cur->buffers[0].string[end + 1], &cur->buffers[0].string[start], (unsigned) cur->buffers[0].length - end + 1); len = end - start; cur->buffers[0].length -= len; bump_lines(cur, _FORMI_USE_CURRENT, - (int) len, TRUE); if (cur->cursor_xpos > cur->lines[row].length) cur->cursor_xpos = cur->lines[row].length; break; case REQ_CLR_EOL: row = cur->start_line + cur->cursor_ypos; start = cur->start_char + cur->cursor_xpos; end = cur->lines[row].end; len = end - start; bcopy(&cur->buffers[0].string[end + 1], &cur->buffers[0].string[start], cur->buffers[0].length - end + 1); cur->buffers[0].length -= len; bump_lines(cur, _FORMI_USE_CURRENT, - (int) len, TRUE); if (cur->cursor_xpos > cur->lines[row].length) cur->cursor_xpos = cur->lines[row].length; break; case REQ_CLR_EOF: row = cur->start_line + cur->cursor_ypos; cur->buffers[0].string[cur->start_char + cur->cursor_xpos] = '\0'; cur->buffers[0].length = strlen(cur->buffers[0].string); cur->lines[row].end = cur->buffers[0].length; cur->lines[row].length = cur->lines[row].end - cur->lines[row].start; for (i = cur->start_char + cur->cursor_xpos; i < cur->buffers[0].length; i++) cur->buffers[0].string[i] = cur->pad; break; case REQ_CLR_FIELD: cur->buffers[0].string[0] = '\0'; cur->buffers[0].length = 0; cur->row_count = 1; cur->start_line = 0; cur->cursor_ypos = 0; cur->cursor_xpos = 0; cur->start_char = 0; cur->lines[0].start = 0; cur->lines[0].end = 0; cur->lines[0].length = 0; break; case REQ_OVL_MODE: cur->overlay = 1; break; case REQ_INS_MODE: cur->overlay = 0; break; case REQ_SCR_FLINE: _formi_scroll_fwd(cur, 1); break; case REQ_SCR_BLINE: _formi_scroll_back(cur, 1); break; case REQ_SCR_FPAGE: _formi_scroll_fwd(cur, cur->rows); break; case REQ_SCR_BPAGE: _formi_scroll_back(cur, cur->rows); break; case REQ_SCR_FHPAGE: _formi_scroll_fwd(cur, cur->rows / 2); break; case REQ_SCR_BHPAGE: _formi_scroll_back(cur, cur->rows / 2); break; case REQ_SCR_FCHAR: _formi_hscroll_fwd(cur, 1); break; case REQ_SCR_BCHAR: _formi_hscroll_back(cur, 1); break; case REQ_SCR_HFLINE: _formi_hscroll_fwd(cur, cur->cols); break; case REQ_SCR_HBLINE: _formi_hscroll_back(cur, cur->cols); break; case REQ_SCR_HFHALF: _formi_hscroll_fwd(cur, cur->cols / 2); break; case REQ_SCR_HBHALF: _formi_hscroll_back(cur, cur->cols / 2); break; default: return 0; } #ifdef DEBUG fprintf(dbg, "exit: xpos=%d, start_char=%d, length=%d, allocated=%d\n", cur->cursor_xpos, cur->start_char, cur->buffers[0].length, cur->buffers[0].allocated); fprintf(dbg, "exit: start_line=%d, ypos=%d\n", cur->start_line, cur->cursor_ypos); fprintf(dbg, "exit: string=\"%s\"\n", cur->buffers[0].string); #endif return 1; } /* * Validate the give character by passing it to any type character * checking routines, if they exist. */ int _formi_validate_char(FIELD *field, char c) { int ret_val; if (field->type == NULL) return E_OK; ret_val = E_INVALID_FIELD; _formi_do_char_validation(field, field->type, c, &ret_val); return ret_val; } /* * Perform the validation of the character, invoke all field_type validation * routines. If the field is ok then update ret_val to E_OK otherwise * ret_val is not changed. */ static void _formi_do_char_validation(FIELD *field, FIELDTYPE *type, char c, int *ret_val) { if ((type->flags & _TYPE_IS_LINKED) == _TYPE_IS_LINKED) { _formi_do_char_validation(field, type->link->next, c, ret_val); _formi_do_char_validation(field, type->link->prev, c, ret_val); } else { if (type->char_check == NULL) *ret_val = E_OK; else { if (type->char_check((int)(unsigned char) c, field->args) == TRUE) *ret_val = E_OK; } } } /* * Validate the current field. If the field validation returns success then * return E_OK otherwise return E_INVALID_FIELD. * */ int _formi_validate_field(FORM *form) { FIELD *cur; char *bp; int ret_val, count; if ((form == NULL) || (form->fields == NULL) || (form->fields[0] == NULL)) return E_INVALID_FIELD; cur = form->fields[form->cur_field]; bp = cur->buffers[0].string; count = _formi_skip_blanks(bp, 0); /* check if we have a null field, depending on the nullok flag * this may be acceptable or not.... */ if (cur->buffers[0].string[count] == '\0') { if ((cur->opts & O_NULLOK) == O_NULLOK) return E_OK; else return E_INVALID_FIELD; } /* check if an unmodified field is ok */ if (cur->buf0_status == 0) { if ((cur->opts & O_PASSOK) == O_PASSOK) return E_OK; else return E_INVALID_FIELD; } /* if there is no type then just accept the field */ if (cur->type == NULL) return E_OK; ret_val = E_INVALID_FIELD; _formi_do_validation(cur, cur->type, &ret_val); return ret_val; } /* * Perform the validation of the field, invoke all field_type validation * routines. If the field is ok then update ret_val to E_OK otherwise * ret_val is not changed. */ static void _formi_do_validation(FIELD *field, FIELDTYPE *type, int *ret_val) { if ((type->flags & _TYPE_IS_LINKED) == _TYPE_IS_LINKED) { _formi_do_validation(field, type->link->next, ret_val); _formi_do_validation(field, type->link->prev, ret_val); } else { if (type->field_check == NULL) *ret_val = E_OK; else { if (type->field_check(field, field_buffer(field, 0)) == TRUE) *ret_val = E_OK; } } } /* * Select the next/previous choice for the field, the driver command * selecting the direction will be passed in c. Return 1 if a choice * selection succeeded, 0 otherwise. */ int _formi_field_choice(FORM *form, int c) { FIELDTYPE *type; FIELD *field; if ((form == NULL) || (form->fields == NULL) || (form->fields[0] == NULL) || (form->fields[form->cur_field]->type == NULL)) return 0; field = form->fields[form->cur_field]; type = field->type; switch (c) { case REQ_NEXT_CHOICE: if (type->next_choice == NULL) return 0; else return type->next_choice(field, field_buffer(field, 0)); case REQ_PREV_CHOICE: if (type->prev_choice == NULL) return 0; else return type->prev_choice(field, field_buffer(field, 0)); default: /* should never happen! */ return 0; } } /* * Update the fields if they have changed. The parameter old has the * previous current field as the current field may have been updated by * the driver. Return 1 if the form page needs updating. * */ int _formi_update_field(FORM *form, int old_field) { int cur, i; cur = form->cur_field; if (old_field != cur) { if (!((cur >= form->page_starts[form->page].first) && (cur <= form->page_starts[form->page].last))) { /* not on same page any more */ for (i = 0; i < form->max_page; i++) { if ((form->page_starts[i].in_use == 1) && (form->page_starts[i].first <= cur) && (form->page_starts[i].last >= cur)) { form->page = i; return 1; } } } } _formi_redraw_field(form, old_field); _formi_redraw_field(form, form->cur_field); return 0; } /* * Compare function for the field sorting * */ static int field_sort_compare(const void *one, const void *two) { const FIELD *a, *b; int tl; /* LINTED const castaway; we don't modify these! */ a = (const FIELD *) *((const FIELD **) one); b = (const FIELD *) *((const FIELD **) two); if (a == NULL) return 1; if (b == NULL) return -1; /* * First check the page, we want the fields sorted by page. * */ if (a->page != b->page) return ((a->page > b->page)? 1 : -1); tl = _formi_top_left(a->parent, a->index, b->index); /* * sort fields left to right, top to bottom so the top left is * the less than value.... */ return ((tl == a->index)? -1 : 1); } /* * Sort the fields in a form ready for driver traversal. */ void _formi_sort_fields(FORM *form) { FIELD **sort_area; int i; CIRCLEQ_INIT(&form->sorted_fields); if ((sort_area = (FIELD **) malloc(sizeof(FIELD *) * form->field_count)) == NULL) return; bcopy(form->fields, sort_area, sizeof(FIELD *) * form->field_count); qsort(sort_area, (unsigned) form->field_count, sizeof(FIELD *), field_sort_compare); for (i = 0; i < form->field_count; i++) CIRCLEQ_INSERT_TAIL(&form->sorted_fields, sort_area[i], glue); free(sort_area); } /* * Set the neighbours for all the fields in the given form. */ void _formi_stitch_fields(FORM *form) { int above_row, below_row, end_above, end_below, cur_row, real_end; FIELD *cur, *above, *below; /* * check if the sorted fields circle queue is empty, just * return if it is. */ if (CIRCLEQ_EMPTY(&form->sorted_fields)) return; /* initially nothing is above..... */ above_row = -1; end_above = TRUE; above = NULL; /* set up the first field as the current... */ cur = CIRCLEQ_FIRST(&form->sorted_fields); cur_row = cur->form_row; /* find the first field on the next row if any */ below = CIRCLEQ_NEXT(cur, glue); below_row = -1; end_below = TRUE; real_end = TRUE; while (below != (void *)&form->sorted_fields) { if (below->form_row != cur_row) { below_row = below->form_row; end_below = FALSE; real_end = FALSE; break; } below = CIRCLEQ_NEXT(below, glue); } /* walk the sorted fields, setting the neighbour pointers */ while (cur != (void *) &form->sorted_fields) { if (cur == CIRCLEQ_FIRST(&form->sorted_fields)) cur->left = NULL; else cur->left = CIRCLEQ_PREV(cur, glue); if (cur == CIRCLEQ_LAST(&form->sorted_fields)) cur->right = NULL; else cur->right = CIRCLEQ_NEXT(cur, glue); if (end_above == TRUE) cur->up = NULL; else { cur->up = above; above = CIRCLEQ_NEXT(above, glue); if (above_row != above->form_row) { end_above = TRUE; above_row = above->form_row; } } if (end_below == TRUE) cur->down = NULL; else { cur->down = below; below = CIRCLEQ_NEXT(below, glue); if (below == (void *) &form->sorted_fields) { end_below = TRUE; real_end = TRUE; } else if (below_row != below->form_row) { end_below = TRUE; below_row = below->form_row; } } cur = CIRCLEQ_NEXT(cur, glue); if ((cur != (void *) &form->sorted_fields) && (cur_row != cur->form_row)) { cur_row = cur->form_row; if (end_above == FALSE) { for (; above != CIRCLEQ_FIRST(&form->sorted_fields); above = CIRCLEQ_NEXT(above, glue)) { if (above->form_row != above_row) { above_row = above->form_row; break; } } } else if (above == NULL) { above = CIRCLEQ_FIRST(&form->sorted_fields); end_above = FALSE; above_row = above->form_row; } else end_above = FALSE; if (end_below == FALSE) { while (below_row == below->form_row) { below = CIRCLEQ_NEXT(below, glue); if (below == (void *)&form->sorted_fields) { real_end = TRUE; end_below = TRUE; break; } } if (below != (void *)&form->sorted_fields) below_row = below->form_row; } else if (real_end == FALSE) end_below = FALSE; } } }