468 lines
12 KiB
C
468 lines
12 KiB
C
|
/* $NetBSD: driver.c,v 1.17 2010/02/03 15:34:43 roy 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.17 2010/02/03 15:34:43 roy 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 = CIRCLEQ_FIRST(&form->sorted_fields);
|
||
|
form->cur_field = fieldp->index;
|
||
|
update_field = 1;
|
||
|
break;
|
||
|
|
||
|
case REQ_SLAST_FIELD:
|
||
|
fieldp = CIRCLEQ_LAST(&form->sorted_fields);
|
||
|
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;
|
||
|
}
|