NetBSD/lib/libform/driver.c
2013-11-26 01:17:00 +00:00

469 lines
12 KiB
C

/* $NetBSD: driver.c,v 1.18 2013/11/26 01:17:00 christos 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 <sys/cdefs.h>
__RCSID("$NetBSD: driver.c,v 1.18 2013/11/26 01:17:00 christos Exp $");
#include <ctype.h>
#include "form.h"
#include "internals.h"
static int
traverse_form_links(FORM *form, int direction);
/*
* Traverse the links of the current field in the given direction until
* either a active & visible field is found or we return to the current
* field. Direction is the REQ_{LEFT,RIGHT,UP,DOWN}_FIELD driver commands.
* The function returns E_OK if a valid field is found, E_REQUEST_DENIED
* otherwise.
*/
static int
traverse_form_links(FORM *form, int direction)
{
unsigned idx;
idx = form->cur_field;
do {
switch (direction) {
case REQ_LEFT_FIELD:
if (form->fields[idx]->left == NULL)
return E_REQUEST_DENIED;
idx = form->fields[idx]->left->index;
break;
case REQ_RIGHT_FIELD:
if (form->fields[idx]->right == NULL)
return E_REQUEST_DENIED;
idx = form->fields[idx]->right->index;
break;
case REQ_UP_FIELD:
if (form->fields[idx]->up == NULL)
return E_REQUEST_DENIED;
idx = form->fields[idx]->up->index;
break;
case REQ_DOWN_FIELD:
if (form->fields[idx]->down == NULL)
return E_REQUEST_DENIED;
idx = form->fields[idx]->down->index;
break;
default:
return E_REQUEST_DENIED;
}
if ((form->fields[idx]->opts & (O_ACTIVE | O_VISIBLE))
== (O_ACTIVE | O_VISIBLE)) {
form->cur_field = idx;
return E_OK;
}
} while (idx != form->cur_field);
return E_REQUEST_DENIED;
}
int
form_driver(FORM *form, int c)
{
FIELD *fieldp;
int update_page, update_field, old_field, old_page, status;
int start_field;
unsigned int pos;
if (form == NULL)
return E_BAD_ARGUMENT;
if ((form->fields == NULL) || (*(form->fields) == NULL))
return E_INVALID_FIELD;
if (form->posted != 1)
return E_NOT_POSTED;
if (form->in_init == 1)
return E_BAD_STATE;
old_field = start_field = form->cur_field;
fieldp = form->fields[form->cur_field];
update_page = update_field = 0;
status = E_OK;
if (c < REQ_MIN_REQUEST) {
if (isprint(c) || isblank(c)) {
do {
pos = fieldp->start_char + fieldp->row_xpos;
/* check if we are allowed to edit this field */
if ((fieldp->opts & O_EDIT) != O_EDIT)
return E_REQUEST_DENIED;
if ((status =
(_formi_add_char(fieldp, pos, c)))
== E_REQUEST_DENIED) {
/*
* Need to check here if we
* want to autoskip. we
* call the form driver
* recursively to pos us on
* the next field and then
* we loop back to ensure
* the next field selected
* can have data added to it
*/
if ((fieldp->opts & O_AUTOSKIP)
!= O_AUTOSKIP)
return E_REQUEST_DENIED;
status = form_driver(form,
REQ_NEXT_FIELD);
if (status != E_OK)
return status;
/*
* check if we have looped
* around all the fields.
* This can easily happen if
* all the fields are full.
*/
if (start_field == form->cur_field)
return E_REQUEST_DENIED;
old_field = form->cur_field;
fieldp = form->fields[form->cur_field];
status = _formi_add_char(fieldp,
fieldp->start_char
+ fieldp->cursor_xpos,
c);
} else if (status == E_INVALID_FIELD)
/* char failed validation, just
* return the status.
*/
return status;
else if (status == E_NO_ROOM)
/* we will get this if the line
* wrapping fails. Deny the
* request.
*/
return E_REQUEST_DENIED;
}
while (status != E_OK);
update_field = (status == E_OK);
} else
return E_REQUEST_DENIED;
} else {
if (c > REQ_MAX_COMMAND)
return E_UNKNOWN_COMMAND;
if ((c >= REQ_NEXT_PAGE) && (c <= REQ_DOWN_FIELD)) {
/* first check the field we are in is ok */
if (_formi_validate_field(form) != E_OK)
return E_INVALID_FIELD;
if (form->field_term != NULL)
form->field_term(form);
/*
* if we have a page movement then the form term
* needs to be called too
*/
if ((c <= REQ_LAST_PAGE) && (form->form_term != NULL))
form->form_term(form);
}
switch (c) {
case REQ_NEXT_PAGE:
if (form->page < form->max_page) {
old_page = form->page;
form->page++;
update_page = 1;
if (_formi_pos_first_field(form) != E_OK) {
form->page = old_page;
status = E_REQUEST_DENIED;
}
} else
status = E_REQUEST_DENIED;
break;
case REQ_PREV_PAGE:
if (form->page > 0) {
old_page = form->page;
form->page--;
update_page = 1;
if (_formi_pos_first_field(form) != E_OK) {
form->page = old_page;
status = E_REQUEST_DENIED;
}
} else
status = E_REQUEST_DENIED;
break;
case REQ_FIRST_PAGE:
old_page = form->page;
form->page = 0;
update_page = 1;
if (_formi_pos_first_field(form) != E_OK) {
form->page = old_page;
status = E_REQUEST_DENIED;
}
break;
case REQ_LAST_PAGE:
old_page = form->page;
form->page = form->max_page - 1;
update_page = 1;
if (_formi_pos_first_field(form) != E_OK) {
form->page = old_page;
status = E_REQUEST_DENIED;
}
break;
case REQ_NEXT_FIELD:
status = _formi_pos_new_field(form, _FORMI_FORWARD,
FALSE);
update_field = 1;
break;
case REQ_PREV_FIELD:
status = _formi_pos_new_field(form, _FORMI_BACKWARD,
FALSE);
update_field = 1;
break;
case REQ_FIRST_FIELD:
form->cur_field = 0;
update_field = 1;
break;
case REQ_LAST_FIELD:
form->cur_field = form->field_count - 1;
update_field = 1;
break;
case REQ_SNEXT_FIELD:
status = _formi_pos_new_field(form, _FORMI_FORWARD,
TRUE);
update_field = 1;
break;
case REQ_SPREV_FIELD:
status = _formi_pos_new_field(form, _FORMI_BACKWARD,
TRUE);
update_field = 1;
break;
case REQ_SFIRST_FIELD:
fieldp = TAILQ_FIRST(&form->sorted_fields);
form->cur_field = fieldp->index;
update_field = 1;
break;
case REQ_SLAST_FIELD:
fieldp = TAILQ_LAST(&form->sorted_fields,
_formi_sort_head);
form->cur_field = fieldp->index;
update_field = 1;
break;
/*
* The up, down, left and right field traversals
* are rolled up into a single function, allow a
* fall through to that function.
*/
case REQ_LEFT_FIELD:
case REQ_RIGHT_FIELD:
case REQ_UP_FIELD:
case REQ_DOWN_FIELD:
status = traverse_form_links(form, c);
update_field = 1;
break;
/* the following commands modify the buffer, check if
this is allowed first before falling through. */
case REQ_DEL_PREV:
/*
* need to check for the overloading of this
* request. If overload flag set and we are
* at the start of field this request turns
* into a previous field request. Otherwise
* fallthrough to the field handler.
*/
if ((form->opts & O_BS_OVERLOAD) == O_BS_OVERLOAD) {
if ((fieldp->start_char == 0) &&
(fieldp->start_line == 0) &&
(fieldp->row_xpos == 0)) {
update_field =
_formi_manipulate_field(form,
REQ_PREV_FIELD);
break;
}
}
/* FALLTHROUGH */
case REQ_NEW_LINE:
/*
* need to check for the overloading of this
* request. If overload flag set and we are
* at the start of field this request turns
* into a next field request. Otherwise
* fallthrough to the field handler.
*/
if ((form->opts & O_NL_OVERLOAD) == O_NL_OVERLOAD) {
if ((fieldp->start_char == 0) &&
(fieldp->start_line == 0) &&
(fieldp->row_xpos == 0)) {
update_field =
_formi_manipulate_field(form,
REQ_NEXT_FIELD);
break;
}
}
/* FALLTHROUGH */
case REQ_INS_CHAR:
case REQ_INS_LINE:
case REQ_DEL_CHAR:
case REQ_DEL_LINE:
case REQ_DEL_WORD:
case REQ_CLR_EOL:
case REQ_CLR_EOF:
case REQ_CLR_FIELD:
case REQ_OVL_MODE:
case REQ_INS_MODE:
/* check if we are allowed to edit the field and fall
* through if we are.
*/
if ((form->fields[form->cur_field]->opts & O_EDIT) != O_EDIT)
return E_REQUEST_DENIED;
/* the following manipulate the field contents, bundle
them into one function.... */
/* FALLTHROUGH */
case REQ_NEXT_CHAR:
case REQ_PREV_CHAR:
case REQ_NEXT_LINE:
case REQ_PREV_LINE:
case REQ_NEXT_WORD:
case REQ_PREV_WORD:
case REQ_BEG_FIELD:
case REQ_END_FIELD:
case REQ_BEG_LINE:
case REQ_END_LINE:
case REQ_LEFT_CHAR:
case REQ_RIGHT_CHAR:
case REQ_UP_CHAR:
case REQ_DOWN_CHAR:
case REQ_SCR_FLINE:
case REQ_SCR_BLINE:
case REQ_SCR_FPAGE:
case REQ_SCR_BPAGE:
case REQ_SCR_FHPAGE:
case REQ_SCR_BHPAGE:
case REQ_SCR_FCHAR:
case REQ_SCR_BCHAR:
case REQ_SCR_HFLINE:
case REQ_SCR_HBLINE:
case REQ_SCR_HFHALF:
case REQ_SCR_HBHALF:
update_field = _formi_manipulate_field(form, c);
break;
case REQ_VALIDATION:
return _formi_validate_field(form);
/* NOTREACHED */
break;
case REQ_PREV_CHOICE:
case REQ_NEXT_CHOICE:
update_field = _formi_field_choice(form, c);
/* reinit the cursor pos just in case */
if (update_field == 1) {
_formi_init_field_xpos(fieldp);
fieldp->row_xpos = 0;
}
break;
default: /* should not need to do this, but.... */
return E_UNKNOWN_COMMAND;
/* NOTREACHED */
break;
}
}
/* call the field and form init functions if required. */
if ((c >= REQ_NEXT_PAGE) && (c <= REQ_DOWN_FIELD)) {
if (form->field_init != NULL)
form->field_init(form);
/*
* if we have a page movement then the form init
* needs to be called too
*/
if ((c <= REQ_LAST_PAGE) && (form->form_init != NULL))
form->form_init(form);
/*
* if there was an error just return now...
*/
if (status != E_OK)
return status;
/* if we have no error, reset the various offsets */
fieldp = form->fields[form->cur_field];
fieldp->start_char = 0;
fieldp->start_line = fieldp->alines;
fieldp->cur_line = fieldp->alines;
fieldp->row_xpos = 0;
fieldp->cursor_ypos = 0;
_formi_init_field_xpos(fieldp);
}
if (update_field < 0)
return update_field;
if (update_field == 1)
update_page |= _formi_update_field(form, old_field);
if (update_page == 1)
_formi_draw_page(form);
pos_form_cursor(form);
if ((update_page == 1) || (update_field == 1))
wrefresh(form->scrwin);
return E_OK;
}