No Minix-specific changes needed.
Change-Id: Ia8ddbdb57ac04dfb42d79c374b9e25b189f9dc3b
./usr/include/float.h minix-sys
./usr/include/fmtmsg.h minix-sys
./usr/include/fnmatch.h minix-sys
+./usr/include/form.h minix-sys
./usr/include/fstab.h minix-sys
./usr/include/fts.h minix-sys
./usr/include/ftw.h minix-sys
./usr/lib/libexec_pic.a minix-sys
./usr/lib/libfetch.a minix-sys
./usr/lib/libfl.a minix-sys
+./usr/lib/libform.a minix-sys
+./usr/lib/libform.so minix-sys
+./usr/lib/libform.so.6 minix-sys
+./usr/lib/libform.so.6.0 minix-sys
+./usr/lib/libform_pic.a minix-sys
./usr/lib/libgcc.a minix-sys gcc=45
./usr/lib/libgcc_eh.a minix-sys gcccmds
./usr/lib/libgcc_s.a minix-sys gcccmds
./usr/man/man3/ctime_r.3 minix-sys
./usr/man/man3/ctime_rz.3 minix-sys
./usr/man/man3/ctype.3 minix-sys
+./usr/man/man3/current_field.3 minix-sys
./usr/man/man3/current_item.3 minix-sys
./usr/man/man3/curses.3 minix-sys
./usr/man/man3/curses_addch.3 minix-sys
./usr/man/man3/curses_window.3 minix-sys
./usr/man/man3/curs_set.3 minix-sys
./usr/man/man3/daemon.3 minix-sys
+./usr/man/man3/data_ahead.3 minix-sys
+./usr/man/man3/data_behind.3 minix-sys
./usr/man/man3/daylight.3 minix-sys
./usr/man/man3/db.3 minix-sys
./usr/man/man3/dbm_clearerr.3 minix-sys
./usr/man/man3/doupdate.3 minix-sys
./usr/man/man3/dprintf.3 minix-sys
./usr/man/man3/drand48.3 minix-sys
+./usr/man/man3/dup_field.3 minix-sys
./usr/man/man3/dupwin.3 minix-sys
+./usr/man/man3/dynamic_field_info.3 minix-sys
./usr/man/man3/easprintf.3 minix-sys
./usr/man/man3/ecalloc.3 minix-sys
./usr/man/man3/echo.3 minix-sys
./usr/man/man3/fgetwc.3 minix-sys
./usr/man/man3/fgetwln.3 minix-sys
./usr/man/man3/fgetws.3 minix-sys
+./usr/man/man3/field_arg.3 minix-sys
+./usr/man/man3/field_back.3 minix-sys
+./usr/man/man3/field_buffer.3 minix-sys
+./usr/man/man3/field_count.3 minix-sys
+./usr/man/man3/field_fore.3 minix-sys
+./usr/man/man3/field_index.3 minix-sys
+./usr/man/man3/field_info.3 minix-sys
+./usr/man/man3/field_init.3 minix-sys
+./usr/man/man3/field_just.3 minix-sys
+./usr/man/man3/field_opts.3 minix-sys
+./usr/man/man3/field_opts_off.3 minix-sys
+./usr/man/man3/field_opts_on.3 minix-sys
+./usr/man/man3/field_pad.3 minix-sys
+./usr/man/man3/field_status.3 minix-sys
+./usr/man/man3/field_term.3 minix-sys
+./usr/man/man3/field_type.3 minix-sys
+./usr/man/man3/field_userptr.3 minix-sys
./usr/man/man3/fileno.3 minix-sys
./usr/man/man3/finite.3 minix-sys
./usr/man/man3/finitef.3 minix-sys
./usr/man/man3/fnmatch.3 minix-sys
./usr/man/man3/fopen.3 minix-sys
./usr/man/man3/forkpty.3 minix-sys
+./usr/man/man3/form.3 minix-sys
+./usr/man/man3/form_cursor.3 minix-sys
+./usr/man/man3/form_data.3 minix-sys
+./usr/man/man3/form_driver.3 minix-sys
+./usr/man/man3/form_field.3 minix-sys
+./usr/man/man3/form_field_attributes.3 minix-sys
+./usr/man/man3/form_field_buffer.3 minix-sys
+./usr/man/man3/form_field_info.3 minix-sys
+./usr/man/man3/form_field_just.3 minix-sys
+./usr/man/man3/form_field_new.3 minix-sys
+./usr/man/man3/form_field_opts.3 minix-sys
+./usr/man/man3/form_field_userptr.3 minix-sys
+./usr/man/man3/form_field_validation.3 minix-sys
+./usr/man/man3/form_fields.3 minix-sys
+./usr/man/man3/form_fieldtype.3 minix-sys
+./usr/man/man3/form_hook.3 minix-sys
+./usr/man/man3/form_init.3 minix-sys
+./usr/man/man3/form_max_page.3 minix-sys
+./usr/man/man3/form_new.3 minix-sys
+./usr/man/man3/form_new_page.3 minix-sys
+./usr/man/man3/form_opts.3 minix-sys
+./usr/man/man3/form_opts_off.3 minix-sys
+./usr/man/man3/form_opts_on.3 minix-sys
+./usr/man/man3/form_page.3 minix-sys
+./usr/man/man3/form_post.3 minix-sys
+./usr/man/man3/form_sub.3 minix-sys
+./usr/man/man3/form_term.3 minix-sys
+./usr/man/man3/form_userptr.3 minix-sys
+./usr/man/man3/form_win.3 minix-sys
+./usr/man/man3/forms.3 minix-sys
./usr/man/man3/fparseln.3 minix-sys
./usr/man/man3/fpclassify.3 minix-sys
./usr/man/man3/fpgetmask.3 minix-sys
./usr/man/man3/fputws.3 minix-sys
./usr/man/man3/fread.3 minix-sys
./usr/man/man3/free.3 minix-sys
+./usr/man/man3/free_field.3 minix-sys
+./usr/man/man3/free_fieldtype.3 minix-sys
+./usr/man/man3/free_form.3 minix-sys
./usr/man/man3/free_item.3 minix-sys
./usr/man/man3/free_menu.3 minix-sys
./usr/man/man3/freeaddrinfo.3 minix-sys
./usr/man/man3/libmagic.3 minix-sys
./usr/man/man3/link_addr.3 minix-sys
./usr/man/man3/linkaddr.3 minix-sys
+./usr/man/man3/link_field.3 minix-sys
+./usr/man/man3/link_fieldtype.3 minix-sys
./usr/man/man3/link_ntoa.3 minix-sys
./usr/man/man3/linkntoa.3 minix-sys
./usr/man/man3/llabs.3 minix-sys
./usr/man/man3/mktime_z.3 minix-sys
./usr/man/man3/modf.3 minix-sys
./usr/man/man3/move.3 minix-sys
+./usr/man/man3/move_field.3 minix-sys
./usr/man/man3/mpool.3 minix-sys
./usr/man/man3/mpool_close.3 minix-sys
./usr/man/man3/mpool_filter.3 minix-sys
./usr/man/man3/napms.3 minix-sys
./usr/man/man3/ndbm.3 minix-sys
./usr/man/man3/network.3 minix-sys
+./usr/man/man3/new_field.3 minix-sys
+./usr/man/man3/new_fieldtype.3 minix-sys
+./usr/man/man3/new_form.3 minix-sys
./usr/man/man3/new_item.3 minix-sys
./usr/man/man3/new_menu.3 minix-sys
+./usr/man/man3/new_page.3 minix-sys
./usr/man/man3/newpad.3 minix-sys
./usr/man/man3/newterm.3 minix-sys
./usr/man/man3/newwin.3 minix-sys
./usr/man/man3/popcountl.3 minix-sys
./usr/man/man3/popcountll.3 minix-sys
./usr/man/man3/popen.3 minix-sys
+./usr/man/man3/pos_form_cursor.3 minix-sys
./usr/man/man3/pos_menu_cursor.3 minix-sys
./usr/man/man3/posix2time.3 minix-sys
./usr/man/man3/posix2time_z.3 minix-sys
./usr/man/man3/posix_spawn_file_actions_destroy.3 minix-sys
./usr/man/man3/posix_spawn_file_actions_init.3 minix-sys
./usr/man/man3/posix_spawnp.3 minix-sys
+./usr/man/man3/post_form.3 minix-sys
./usr/man/man3/post_menu.3 minix-sys
./usr/man/man3/pow.3 minix-sys
./usr/man/man3/powf.3 minix-sys
./usr/man/man3/scalbf.3 minix-sys
./usr/man/man3/scalbn.3 minix-sys
./usr/man/man3/scalbnf.3 minix-sys
+./usr/man/man3/scale_form.3 minix-sys
./usr/man/man3/scale_menu.3 minix-sys
./usr/man/man3/scandir.3 minix-sys
./usr/man/man3/scanf.3 minix-sys
./usr/man/man3/secure_path.3 minix-sys
./usr/man/man3/seed48.3 minix-sys
./usr/man/man3/seekdir.3 minix-sys
+./usr/man/man3/set_current_field.3 minix-sys
./usr/man/man3/set_current_item.3 minix-sys
+./usr/man/man3/set_field_back.3 minix-sys
+./usr/man/man3/set_field_buffer.3 minix-sys
+./usr/man/man3/set_field_fore.3 minix-sys
+./usr/man/man3/set_field_init.3 minix-sys
+./usr/man/man3/set_field_just.3 minix-sys
+./usr/man/man3/set_field_opts.3 minix-sys
+./usr/man/man3/set_field_pad.3 minix-sys
+./usr/man/man3/set_field_printf.3 minix-sys
+./usr/man/man3/set_field_status.3 minix-sys
+./usr/man/man3/set_field_term.3 minix-sys
+./usr/man/man3/set_field_type.3 minix-sys
+./usr/man/man3/set_field_userptr.3 minix-sys
+./usr/man/man3/set_fieldtype_arg.3 minix-sys
+./usr/man/man3/set_fieldtype_choice.3 minix-sys
+./usr/man/man3/set_form_fields.3 minix-sys
+./usr/man/man3/set_form_init.3 minix-sys
+./usr/man/man3/set_form_opts.3 minix-sys
+./usr/man/man3/set_form_page.3 minix-sys
+./usr/man/man3/set_form_sub.3 minix-sys
+./usr/man/man3/set_form_term.3 minix-sys
+./usr/man/man3/set_form_userptr.3 minix-sys
+./usr/man/man3/set_form_win.3 minix-sys
./usr/man/man3/set_item_init.3 minix-sys
./usr/man/man3/set_item_term.3 minix-sys
./usr/man/man3/set_item_userptr.3 minix-sys
./usr/man/man3/set_item_value.3 minix-sys
+./usr/man/man3/set_max_field.3 minix-sys
./usr/man/man3/set_menu_back.3 minix-sys
./usr/man/man3/set_menu_fore.3 minix-sys
./usr/man/man3/set_menu_format.3 minix-sys
./usr/man/man3/set_menu_unmark.3 minix-sys
./usr/man/man3/set_menu_userptr.3 minix-sys
./usr/man/man3/set_menu_win.3 minix-sys
+./usr/man/man3/set_new_page.3 minix-sys
./usr/man/man3/set_top_row.3 minix-sys
./usr/man/man3/setbuf.3 minix-sys
./usr/man/man3/setbuffer.3 minix-sys
./usr/man/man3/ungetch.3 minix-sys
./usr/man/man3/ungetwc.3 minix-sys
./usr/man/man3/unlockpt.3 minix-sys
+./usr/man/man3/unpost_form.3 minix-sys
./usr/man/man3/unpost_menu.3 minix-sys
./usr/man/man3/unsetenv.3 minix-sys
./usr/man/man3/untouchwin.3 minix-sys
.endif
.endif # (defined(__MINIX) && ${MKGCCCMDS} == "yes")
-#SUBDIR+= libform # depends on libcurses
+SUBDIR+= libform # depends on libcurses
SUBDIR+= libmenu # depends on libcurses
#SUBDIR+= libradius # depends on libcrypto if (${MKCRYPTO} != "no")
.if (${MKRUMP} != "no")
--- /dev/null
+# $NetBSD: Makefile,v 1.10 2012/03/21 05:38:49 matt Exp $
+#
+
+WARNS= 2
+
+CPPFLAGS+=-I${.CURDIR}
+.if defined(DEBUG_FORMS)
+CFLAGS+=-gdwarf-2
+CPPFLAGS+=-DDEBUG
+LDFLAGS+=-g
+.endif
+LIB= form
+
+LIBDPLIBS+= curses ${.CURDIR}/../libcurses
+
+SRCS= driver.c field_types.c internals.c field.c form.c post.c type_alnum.c \
+ type_alpha.c type_integer.c type_numeric.c type_enum.c type_regex.c \
+ type_ipv4.c type_ipv6.c
+MAN= form_cursor.3 form_data.3 form_driver.3 form_field.3 \
+ form_field_attributes.3 form_field_buffer.3 form_field_info.3 \
+ form_field_just.3 form_field_new.3 form_field_opts.3 \
+ form_field_userptr.3 form_field_validation.3 form_fieldtype.3 \
+ form_hook.3 form_new.3 form_new_page.3 form_opts.3 form_page.3 \
+ form_post.3 form_userptr.3 form_win.3 forms.3
+MLINKS+= forms.3 form.3 form_page.3 current_field.3 \
+ form_data.3 data_ahead.3 form_field_new.3 dup_field.3 \
+ form_field_info.3 dynamic_field_info.3 \
+ form_page.3 current_field.3 form_data.3 data_ahead.3 \
+ form_data.3 data_behind.3 form_field_new.3 dup_field.3 \
+ form_field_info.3 dynamic_field_info.3 \
+ form_field_validation.3 field_arg.3 \
+ form_field_attributes.3 field_back.3 \
+ form_field_buffer.3 field_buffer.3 \
+ form_field.3 field_count.3 \
+ form_field_attributes.3 field_fore.3 \
+ form_page.3 field_index.3 form_field_info.3 field_info.3 \
+ form_hook.3 field_init.3 form_field_just.3 field_just.3 \
+ form_field_opts.3 field_opts.3 \
+ form_field_opts.3 field_opts_off.3 \
+ form_field_opts.3 field_opts_on.3 \
+ form_field_attributes.3 field_pad.3 \
+ form_field_buffer.3 field_status.3 form_hook.3 field_term.3 \
+ form_field_validation.3 field_type.3 \
+ form_field_userptr.3 field_userptr.3 \
+ form_field.3 form_fields.3 \
+ form_hook.3 form_init.3 form_page.3 form_max_page.3 \
+ form_opts.3 form_opts_off.3 \
+ form_opts.3 form_opts_on.3 \
+ form_win.3 form_sub.3 form_hook.3 form_term.3 \
+ form_field_new.3 free_field.3 \
+ form_fieldtype.3 free_fieldtype.3 \
+ form_new.3 free_form.3 form_field_new.3 link_field.3 \
+ form_fieldtype.3 link_fieldtype.3 form_field.3 move_field.3 \
+ form_field_new.3 new_field.3 \
+ form_fieldtype.3 new_fieldtype.3 \
+ form_new.3 new_form.3 form_new_page.3 new_page.3 \
+ form_cursor.3 pos_form_cursor.3 form_post.3 post_form.3 \
+ form_win.3 scale_form.3 form_page.3 set_current_field.3 \
+ form_field_attributes.3 set_field_back.3 \
+ form_field_buffer.3 set_field_buffer.3 \
+ form_field_attributes.3 set_field_fore.3 \
+ form_hook.3 set_field_init.3 \
+ form_field_just.3 set_field_just.3 \
+ form_field_opts.3 set_field_opts.3 \
+ form_field_attributes.3 set_field_pad.3 \
+ form_field_buffer.3 set_field_printf.3 \
+ form_field_buffer.3 set_field_status.3 \
+ form_hook.3 set_field_term.3 \
+ form_field_validation.3 set_field_type.3 \
+ form_field_userptr.3 set_field_userptr.3 \
+ form_fieldtype.3 set_fieldtype_arg.3 \
+ form_fieldtype.3 set_fieldtype_choice.3 \
+ form_field.3 set_form_fields.3 \
+ form_hook.3 set_form_init.3 form_opts.3 set_form_opts.3 \
+ form_page.3 set_form_page.3 form_win.3 set_form_sub.3 \
+ form_hook.3 set_form_term.3 form_userptr.3 set_form_userptr.3 \
+ form_win.3 set_form_win.3 form_field_buffer.3 set_max_field.3 \
+ form_new_page.3 set_new_page.3 form_post.3 unpost_form.3
+
+INCS= form.h
+INCSDIR=/usr/include
+
+.include <bsd.lib.mk>
+.include <bsd.subdir.mk>
--- /dev/null
+/* $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;
+}
--- /dev/null
+/* $NetBSD: field.c,v 1.25 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: field.c,v 1.25 2010/02/03 15:34:43 roy Exp $");
+
+#include <stdlib.h>
+#include <strings.h>
+#include <stdarg.h>
+#include <form.h>
+#include "internals.h"
+
+extern FORM _formi_default_form;
+
+FIELD _formi_default_field = {
+ 0, /* rows in the field */
+ 0, /* columns in the field */
+ 0, /* dynamic rows */
+ 0, /* dynamic columns */
+ 0, /* maximum growth */
+ 0, /* starting row in the form subwindow */
+ 0, /* starting column in the form subwindow */
+ 0, /* number of off screen rows */
+ 0, /* index of this field in form fields array. */
+ 0, /* number of buffers associated with this field */
+ FALSE, /* set to true if buffer 0 has changed. */
+ NO_JUSTIFICATION, /* justification style of the field */
+ FALSE, /* set to true if field is in overlay mode */
+ NULL, /* pointer to the current line cursor is on */
+ 0, /* starting char in string (horiz scroll) */
+ NULL, /* starting line in field (vert scroll) */
+ 0, /* number of rows actually used in field */
+ 0, /* actual pos of cursor in row, not same as x pos due to tabs */
+ 0, /* x pos of cursor in field */
+ 0, /* y pos of cursor in field */
+ 0, /* start of a new page on the form if 1 */
+ 0, /* number of the page this field is on */
+ A_NORMAL, /* character attributes for the foreground */
+ A_NORMAL, /* character attributes for the background */
+ ' ', /* padding character */
+ DEFAULT_FORM_OPTS, /* options for the field */
+ NULL, /* the form this field is bound to, if any */
+ NULL, /* field above this one */
+ NULL, /* field below this one */
+ NULL, /* field to the left of this one */
+ NULL, /* field to the right of this one */
+ NULL, /* user defined pointer. */
+ NULL, /* used if fields are linked */
+ NULL, /* type struct for the field */
+ {NULL, NULL}, /* circle queue glue for sorting fields */
+ NULL, /* args for field type. */
+ NULL, /* pointer to the array of lines structures. */
+ NULL, /* list of lines available for reuse */
+ NULL, /* array of buffers for the field */
+};
+
+/* internal function prototypes */
+static int
+field_buffer_init(FIELD *field, int buffer, unsigned int len);
+static FIELD *
+_formi_create_field(FIELD *, int, int, int, int, int, int);
+
+
+/*
+ * Set the userptr for the field
+ */
+int
+set_field_userptr(FIELD *field, void *ptr)
+{
+ FIELD *fp = (field == NULL) ? &_formi_default_field : field;
+
+ fp->userptr = ptr;
+
+ return E_OK;
+}
+
+/*
+ * Return the userptr for the field.
+ */
+
+void *
+field_userptr(FIELD *field)
+{
+ if (field == NULL)
+ return _formi_default_field.userptr;
+ else
+ return field->userptr;
+}
+
+/*
+ * Set the options for the designated field.
+ */
+int
+set_field_opts(FIELD *field, Form_Options options)
+{
+ int i;
+
+ FIELD *fp = (field == NULL) ? &_formi_default_field : field;
+
+ /* not allowed to set opts if the field is the current one */
+ if ((field != NULL) && (field->parent != NULL) &&
+ (field->parent->cur_field == field->index))
+ return E_CURRENT;
+
+ if ((options & O_STATIC) == O_STATIC) {
+ for (i = 0; i < fp->nbuf; i++) {
+ if (fp->buffers[i].length > fp->cols)
+ fp->buffers[i].string[fp->cols] = '\0';
+ }
+ }
+
+ fp->opts = options;
+
+ /* if appropriate, redraw the field */
+ if ((field != NULL) && (field->parent != NULL)
+ && (field->parent->posted == 1)) {
+ _formi_redraw_field(field->parent, field->index);
+ pos_form_cursor(field->parent);
+ wrefresh(field->parent->scrwin);
+ }
+
+ return E_OK;
+}
+
+/*
+ * Turn on the passed field options.
+ */
+int
+field_opts_on(FIELD *field, Form_Options options)
+{
+ int i;
+
+ FIELD *fp = (field == NULL) ? &_formi_default_field : field;
+
+ /* not allowed to set opts if the field is the current one */
+ if ((field != NULL) && (field->parent != NULL) &&
+ (field->parent->cur_field == field->index))
+ return E_CURRENT;
+
+ if ((options & O_STATIC) == O_STATIC) {
+ for (i = 0; i < fp->nbuf; i++) {
+ if (fp->buffers[i].length > fp->cols)
+ fp->buffers[i].string[fp->cols] = '\0';
+ }
+ }
+
+ fp->opts |= options;
+
+ /* if appropriate, redraw the field */
+ if ((field != NULL) && (field->parent != NULL)
+ && (field->parent->posted == 1)) {
+ _formi_redraw_field(field->parent, field->index);
+ pos_form_cursor(field->parent);
+ wrefresh(field->parent->scrwin);
+ }
+
+ return E_OK;
+}
+
+/*
+ * Turn off the passed field options.
+ */
+int
+field_opts_off(FIELD *field, Form_Options options)
+{
+ FIELD *fp = (field == NULL) ? &_formi_default_field : field;
+
+ /* not allowed to set opts if the field is the current one */
+ if ((field != NULL) && (field->parent != NULL) &&
+ (field->parent->cur_field == field->index))
+ return E_CURRENT;
+
+ fp->opts &= ~options;
+
+ /* if appropriate, redraw the field */
+ if ((field != NULL) && (field->parent != NULL)
+ && (field->parent->posted == 1)) {
+ _formi_redraw_field(field->parent, field->index);
+ pos_form_cursor(field->parent);
+ wrefresh(field->parent->scrwin);
+ }
+
+ return E_OK;
+}
+
+/*
+ * Return the field options associated with the passed field.
+ */
+Form_Options
+field_opts(FIELD *field)
+{
+ if (field == NULL)
+ return _formi_default_field.opts;
+ else
+ return field->opts;
+}
+
+/*
+ * Set the justification for the passed field.
+ */
+int
+set_field_just(FIELD *field, int justification)
+{
+ FIELD *fp = (field == NULL) ? &_formi_default_field : field;
+
+ /*
+ * not allowed to set justification if the field is
+ * the current one
+ */
+ if ((field != NULL) && (field->parent != NULL) &&
+ (field->parent->cur_field == field->index))
+ return E_CURRENT;
+
+ if ((justification < MIN_JUST_STYLE) /* check justification valid */
+ || (justification > MAX_JUST_STYLE))
+ return E_BAD_ARGUMENT;
+
+ /* only allow justification on static, single row fields */
+ if (((fp->opts & O_STATIC) != O_STATIC) ||
+ ((fp->rows + fp->nrows) > 1))
+ return E_BAD_ARGUMENT;
+
+ fp->justification = justification;
+
+ _formi_init_field_xpos(fp);
+
+ return E_OK;
+}
+
+/*
+ * Return the justification style of the field passed.
+ */
+int
+field_just(FIELD *field)
+{
+ if (field == NULL)
+ return _formi_default_field.justification;
+ else
+ return field->justification;
+}
+
+/*
+ * Return information about the field passed.
+ */
+int
+field_info(FIELD *field, int *rows, int *cols, int *frow, int *fcol,
+ int *nrow, int *nbuf)
+{
+ if (field == NULL)
+ return E_BAD_ARGUMENT;
+
+ *rows = field->rows;
+ *cols = field->cols;
+ *frow = field->form_row;
+ *fcol = field->form_col;
+ *nrow = field->nrows;
+ *nbuf = field->nbuf;
+
+ return E_OK;
+}
+
+/*
+ * Report the dynamic field information.
+ */
+int
+dynamic_field_info(FIELD *field, int *drows, int *dcols, int *max)
+{
+ if (field == NULL)
+ return E_BAD_ARGUMENT;
+
+ if ((field->opts & O_STATIC) == O_STATIC) {
+ *drows = field->rows;
+ *dcols = field->cols;
+ } else {
+ *drows = field->drows;
+ *dcols = field->dcols;
+ }
+
+ *max = field->max;
+
+ return E_OK;
+}
+
+/*
+ * Init all the field variables, perform wrapping and other tasks
+ * after the field buffer is set.
+ */
+static int
+field_buffer_init(FIELD *field, int buffer, unsigned int len)
+{
+ int status;
+ char *newp;
+
+ if (buffer == 0) {
+ field->start_char = 0;
+ field->start_line = 0;
+ field->row_xpos = 0;
+ field->cursor_xpos = 0;
+ field->cursor_ypos = 0;
+ field->row_count = 1; /* must be at least one row XXX need to shift old rows (if any) to free list??? */
+ field->alines->length = len;
+ if ((newp = realloc(field->alines->string,
+ (size_t) len + 1)) == NULL)
+ return E_SYSTEM_ERROR;
+ field->alines->string = newp;
+ field->alines->allocated = len + 1;
+ strlcpy(field->alines->string, field->buffers[buffer].string,
+ (size_t) len + 1);
+ field->alines->expanded =
+ _formi_tab_expanded_length(field->alines->string,
+ 0, field->alines->length);
+
+ field->start_line = field->alines;
+ field->cur_line = field->alines;
+
+ /* we have to hope the wrap works - if it does not then the
+ buffer is pretty much borked */
+ status = _formi_wrap_field(field, field->cur_line);
+ if (status != E_OK)
+ return status;
+
+ /*
+ * calculate the tabs for a single row field, the
+ * multiline case is handled when the wrap is done.
+ */
+ if (field->row_count == 1)
+ _formi_calculate_tabs(field->alines);
+
+ /* redraw the field to reflect the new contents. If the field
+ * is attached....
+ */
+ if ((field->parent != NULL) && (field->parent->posted == 1)) {
+ _formi_redraw_field(field->parent, field->index);
+ /* make sure cursor goes back to current field */
+ pos_form_cursor(field->parent);
+ }
+ }
+
+ return E_OK;
+}
+
+
+/*
+ * Set the field buffer to the string that results from processing
+ * the given format (fmt) using sprintf.
+ */
+int
+set_field_printf(FIELD *field, int buffer, char *fmt, ...)
+{
+ int len;
+ va_list args;
+
+ if (field == NULL)
+ return E_BAD_ARGUMENT;
+
+ if (buffer >= field->nbuf)
+ return E_BAD_ARGUMENT;
+
+ va_start(args, fmt);
+ /* check for buffer already existing, free the storage */
+ if (field->buffers[buffer].allocated != 0)
+ free(field->buffers[buffer].string);
+
+ len = vasprintf(&field->buffers[buffer].string, fmt, args);
+ va_end(args);
+ if (len < 0)
+ return E_SYSTEM_ERROR;
+
+ field->buffers[buffer].length = len;
+ field->buffers[buffer].allocated = len + 1;
+ if (((field->opts & O_STATIC) == O_STATIC) && (len > field->cols)
+ && ((field->rows + field->nrows) == 1))
+ len = field->cols;
+
+ field->buffers[buffer].string[len] = '\0';
+ return field_buffer_init(field, buffer, (unsigned int) len);
+}
+
+/*
+ * Set the value of the field buffer to the value given.
+ */
+
+int
+set_field_buffer(FIELD *field, int buffer, char *value)
+{
+ unsigned int len;
+ int status;
+
+ if (field == NULL)
+ return E_BAD_ARGUMENT;
+
+ if (buffer >= field->nbuf) /* make sure buffer is valid */
+ return E_BAD_ARGUMENT;
+
+ len = (unsigned int) strlen(value);
+ if (((field->opts & O_STATIC) == O_STATIC) && (len > field->cols)
+ && ((field->rows + field->nrows) == 1))
+ len = field->cols;
+
+#ifdef DEBUG
+ if (_formi_create_dbg_file() != E_OK)
+ return E_SYSTEM_ERROR;
+
+ fprintf(dbg,
+ "set_field_buffer: entry: len = %d, value = %s, buffer=%d\n",
+ len, value, buffer);
+ fprintf(dbg, "set_field_buffer: entry: string = ");
+ if (field->buffers[buffer].string != NULL)
+ fprintf(dbg, "%s, len = %d\n", field->buffers[buffer].string,
+ field->buffers[buffer].length);
+ else
+ fprintf(dbg, "(null), len = 0\n");
+ fprintf(dbg, "set_field_buffer: entry: lines.len = %d\n",
+ field->alines[0].length);
+#endif
+
+ if ((field->buffers[buffer].string =
+ (char *) realloc(field->buffers[buffer].string,
+ (size_t) len + 1)) == NULL)
+ return E_SYSTEM_ERROR;
+
+ strlcpy(field->buffers[buffer].string, value, (size_t) len + 1);
+ field->buffers[buffer].length = len;
+ field->buffers[buffer].allocated = len + 1;
+ status = field_buffer_init(field, buffer, len);
+
+#ifdef DEBUG
+ fprintf(dbg, "set_field_buffer: exit: len = %d, value = %s\n",
+ len, value);
+ fprintf(dbg, "set_field_buffer: exit: string = %s, len = %d\n",
+ field->buffers[buffer].string, field->buffers[buffer].length);
+ fprintf(dbg, "set_field_buffer: exit: lines.len = %d\n",
+ field->alines[0].length);
+#endif
+
+ return status;
+}
+
+/*
+ * Return the requested field buffer to the caller.
+ */
+char *
+field_buffer(FIELD *field, int buffer)
+{
+
+ char *reformat, *p;
+ _FORMI_FIELD_LINES *linep;
+
+ if (field == NULL)
+ return NULL;
+
+ if (buffer >= field->nbuf)
+ return NULL;
+
+ /*
+ * We force a sync from the line structs to the buffer here.
+ * Traditional libform say we don't need to because it is
+ * done on a REQ_VALIDATE but NetBSD libform previously did
+ * not enforce this because the buffer contents were always
+ * current. Changes to line handling make this no longer so
+ * - the line structs may contain different data to the
+ * buffer if unsynced.
+ */
+ if (_formi_sync_buffer(field) != E_OK)
+ return NULL;
+
+ if ((field->opts & O_REFORMAT) != O_REFORMAT) {
+ return field->buffers[buffer].string;
+ } else {
+ if (field->row_count > 1) {
+ /* reformat */
+ reformat = (char *)
+ malloc(strlen(field->buffers[buffer].string)
+ + ((field->row_count - 1)
+ * sizeof(char)) + 1);
+
+ if (reformat == NULL)
+ return NULL;
+
+ /*
+ * foreach row copy line, append newline, no
+ * newline on last row.
+ */
+ p = reformat;
+ linep = field->alines;
+
+ do
+ {
+ if (linep->length != 0) {
+ strncpy(p, linep->string,
+ (size_t) linep->length);
+ p += linep->length;
+ }
+
+ linep = linep->next;
+ if (linep != NULL)
+ *p = '\n';
+ p++;
+ }
+ while (linep != NULL);
+
+ p = '\0';
+ return reformat;
+ } else {
+ asprintf(&reformat, "%s",
+ field->buffers[buffer].string);
+ return reformat;
+ }
+ }
+}
+
+/*
+ * Set the buffer 0 field status.
+ */
+int
+set_field_status(FIELD *field, int status)
+{
+
+ if (field == NULL)
+ return E_BAD_ARGUMENT;
+
+ if (status != FALSE)
+ field->buf0_status = TRUE;
+ else
+ field->buf0_status = FALSE;
+
+ return E_OK;
+}
+
+/*
+ * Return the buffer 0 status flag for the given field.
+ */
+int
+field_status(FIELD *field)
+{
+
+ if (field == NULL) /* the default buffer 0 never changes :-) */
+ return FALSE;
+
+ return field->buf0_status;
+}
+
+/*
+ * Set the maximum growth for a dynamic field.
+ */
+int
+set_max_field(FIELD *fptr, int max)
+{
+ FIELD *field = (fptr == NULL)? &_formi_default_field : fptr;
+
+ if ((field->opts & O_STATIC) == O_STATIC) /* check if field dynamic */
+ return E_BAD_ARGUMENT;
+
+ if (max < 0) /* negative numbers are bad.... */
+ return E_BAD_ARGUMENT;
+
+ field->max = max;
+ return E_OK;
+}
+
+/*
+ * Set the field foreground character attributes.
+ */
+int
+set_field_fore(FIELD *fptr, chtype attribute)
+{
+ FIELD *field = (fptr == NULL)? &_formi_default_field : fptr;
+
+ field->fore = attribute;
+ return E_OK;
+}
+
+/*
+ * Return the foreground character attribute for the given field.
+ */
+chtype
+field_fore(FIELD *field)
+{
+ if (field == NULL)
+ return _formi_default_field.fore;
+ else
+ return field->fore;
+}
+
+/*
+ * Set the background character attribute for the given field.
+ */
+int
+set_field_back(FIELD *field, chtype attribute)
+{
+ if (field == NULL)
+ _formi_default_field.back = attribute;
+ else
+ field->back = attribute;
+
+ return E_OK;
+}
+
+/*
+ * Get the background character attribute for the given field.
+ */
+chtype
+field_back(FIELD *field)
+{
+ if (field == NULL)
+ return _formi_default_field.back;
+ else
+ return field->back;
+}
+
+/*
+ * Set the pad character for the given field.
+ */
+int
+set_field_pad(FIELD *field, int pad)
+{
+ if (field == NULL)
+ _formi_default_field.pad = pad;
+ else
+ field->pad = pad;
+
+ return E_OK;
+}
+
+/*
+ * Return the padding character for the given field.
+ */
+int
+field_pad(FIELD *field)
+{
+ if (field == NULL)
+ return _formi_default_field.pad;
+ else
+ return field->pad;
+}
+
+/*
+ * Set the field initialisation function hook.
+ */
+int
+set_field_init(FORM *form, Form_Hook function)
+{
+ if (form == NULL)
+ _formi_default_form.field_init = function;
+ else
+ form->field_init = function;
+
+ return E_OK;
+}
+
+/*
+ * Return the function hook for the field initialisation.
+ */
+Form_Hook
+field_init(FORM *form)
+{
+ if (form == NULL)
+ return _formi_default_form.field_init;
+ else
+ return form->field_init;
+}
+
+/*
+ * Set the field termination function hook.
+ */
+int
+set_field_term(FORM *form, Form_Hook function)
+{
+ if (form == NULL)
+ _formi_default_form.field_term = function;
+ else
+ form->field_term = function;
+
+ return E_OK;
+}
+
+/*
+ * Return the function hook defined for the field termination.
+ */
+Form_Hook
+field_term(FORM *form)
+{
+ if (form == NULL)
+ return _formi_default_form.field_term;
+ else
+ return form->field_term;
+}
+
+/*
+ * Set the page flag on the given field to indicate it is the start of a
+ * new page.
+ */
+int
+set_new_page(FIELD *fptr, int page)
+{
+ FIELD *field = (fptr == NULL)? &_formi_default_field : fptr;
+
+ if (field->parent != NULL) /* check if field is connected to a form */
+ return E_CONNECTED;
+
+ field->page_break = (page != FALSE);
+ return E_OK;
+}
+
+/*
+ * Return the page status for the given field. TRUE is returned if the
+ * field is the start of a new page.
+ */
+int
+new_page(FIELD *field)
+{
+ if (field == NULL)
+ return _formi_default_field.page_break;
+ else
+ return field->page_break;
+}
+
+/*
+ * Return the index of the field in the form fields array.
+ */
+int
+field_index(FIELD *field)
+{
+ if (field == NULL)
+ return E_BAD_ARGUMENT;
+
+ if (field->parent == NULL)
+ return E_NOT_CONNECTED;
+
+ return field->index;
+}
+
+/*
+ * Internal function that does most of the work to create a new field.
+ * The new field is initialised from the information in the prototype
+ * field passed.
+ * Returns NULL on error.
+ */
+static FIELD *
+_formi_create_field(FIELD *prototype, int rows, int cols, int frow,
+ int fcol, int nrows, int nbuf)
+{
+ FIELD *new;
+
+ if ((rows <= 0) || (cols <= 0) || (frow < 0) || (fcol < 0) ||
+ (nrows < 0) || (nbuf < 0))
+ return NULL;
+
+ if ((new = (FIELD *)malloc(sizeof(FIELD))) == NULL) {
+ return NULL;
+ }
+
+ /* copy in the default field info */
+ bcopy(prototype, new, sizeof(FIELD));
+
+ new->nbuf = nbuf + 1;
+ new->rows = rows;
+ new->cols = cols;
+ new->form_row = frow;
+ new->form_col = fcol;
+ new->nrows = nrows;
+ new->link = new;
+ return new;
+}
+
+/*
+ * Create a new field structure.
+ */
+FIELD *
+new_field(int rows, int cols, int frow, int fcol, int nrows, int nbuf)
+{
+ FIELD *new;
+ size_t buf_len;
+ int i;
+
+
+ if ((new = _formi_create_field(&_formi_default_field, rows, cols,
+ frow, fcol, nrows, nbuf)) == NULL)
+ return NULL;
+
+ buf_len = (nbuf + 1) * sizeof(FORM_STR);
+
+ if ((new->buffers = (FORM_STR *)malloc(buf_len)) == NULL) {
+ free(new);
+ return NULL;
+ }
+
+ /* Initialise the strings to a zero length string */
+ for (i = 0; i < nbuf + 1; i++) {
+ if ((new->buffers[i].string =
+ (char *) malloc(sizeof(char))) == NULL) {
+ free(new->buffers);
+ free(new);
+ return NULL;
+ }
+ new->buffers[i].string[0] = '\0';
+ new->buffers[i].length = 0;
+ new->buffers[i].allocated = 1;
+ }
+
+ if ((new->alines = (_FORMI_FIELD_LINES *)
+ malloc(sizeof(struct _formi_field_lines))) == NULL) {
+ free(new->buffers);
+ free(new);
+ return NULL;
+ }
+
+ new->alines->prev = NULL;
+ new->alines->next = NULL;
+ new->alines->allocated = 0;
+ new->alines->length = 0;
+ new->alines->expanded = 0;
+ new->alines->string = NULL;
+ new->alines->hard_ret = FALSE;
+ new->alines->tabs = NULL;
+ new->start_line = new->alines;
+ new->cur_line = new->alines;
+
+ return new;
+}
+
+/*
+ * Duplicate the given field, including it's buffers.
+ */
+FIELD *
+dup_field(FIELD *field, int frow, int fcol)
+{
+ FIELD *new;
+ size_t row_len, buf_len;
+
+ if (field == NULL)
+ return NULL;
+
+ /* XXXX this right???? */
+ if ((new = _formi_create_field(field, (int) field->rows,
+ (int ) field->cols,
+ frow, fcol, (int) field->nrows,
+ field->nbuf - 1)) == NULL)
+ return NULL;
+
+ row_len = (field->rows + field->nrows + 1) * field->cols;
+ buf_len = (field->nbuf + 1) * row_len * sizeof(FORM_STR);
+
+ if ((new->buffers = (FORM_STR *)malloc(buf_len)) == NULL) {
+ free(new);
+ return NULL;
+ }
+
+ /* copy the buffers from the source field into the new copy */
+ bcopy(field->buffers, new->buffers, buf_len);
+
+ return new;
+}
+
+/*
+ * Create a new field at the specified location by duplicating the given
+ * field. The buffers are shared with the parent field.
+ */
+FIELD *
+link_field(FIELD *field, int frow, int fcol)
+{
+ FIELD *new;
+
+ if (field == NULL)
+ return NULL;
+
+ if ((new = _formi_create_field(field, (int) field->rows,
+ (int) field->cols,
+ frow, fcol, (int) field->nrows,
+ field->nbuf - 1)) == NULL)
+ return NULL;
+
+ new->link = field->link;
+ field->link = new;
+
+ /* we are done. The buffer pointer was copied during the field
+ creation. */
+ return new;
+}
+
+/*
+ * Release all storage allocated to the field
+ */
+int
+free_field(FIELD *field)
+{
+ FIELD *flink;
+ int i;
+ _formi_tab_t *ts, *nts;
+
+ if (field == NULL)
+ return E_BAD_ARGUMENT;
+
+ if (field->parent != NULL)
+ return E_CONNECTED;
+
+ if (field->link == field) { /* check if field linked */
+ /* no it is not - release the buffers */
+ free(field->buffers);
+ /* free the tab structures */
+ for (i = 0; i < field->row_count - 1; i++) {
+ if (field->alines[i].tabs != NULL) {
+ ts = field->alines[i].tabs;
+ while (ts != NULL) {
+ nts = ts->fwd;
+ free(ts);
+ ts = nts;
+ }
+ }
+ }
+ } else {
+ /* is linked, traverse the links to find the field referring
+ * to the one to be freed.
+ */
+ for (flink = field->link; flink != field; flink = flink->link);
+ flink->link = field->link;
+ }
+
+ free(field);
+ return E_OK;
+}
+
+
--- /dev/null
+/* $NetBSD: field_types.c,v 1.7 2006/03/19 20:02:27 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: field_types.c,v 1.7 2006/03/19 20:02:27 christos Exp $");
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include "form.h"
+#include "internals.h"
+
+extern FIELD _formi_default_field;
+
+/* function prototypes.... */
+static void
+_formi_create_field_args(FIELDTYPE *type, char **type_args,
+ formi_type_link **link, va_list *args, int *error);
+static FIELDTYPE *
+_formi_create_fieldtype(void);
+
+/*
+ * Process the arguments, if any, for the field type.
+ */
+static void
+_formi_create_field_args(FIELDTYPE *type, char **type_args,
+ formi_type_link **link, va_list *args, int *error)
+{
+ formi_type_link *l;
+
+ l = NULL;
+ if ((type != NULL)
+ && ((type->flags & _TYPE_HAS_ARGS) == _TYPE_HAS_ARGS)) {
+ if ((type->flags & _TYPE_IS_LINKED) == _TYPE_IS_LINKED) {
+ l = malloc(sizeof(*l));
+ if (l != NULL) {
+ _formi_create_field_args(type->link->next,
+ type_args,
+ &type->link->next->link,
+ args,
+ error);
+ _formi_create_field_args(type->link->prev,
+ type_args,
+ &type->link->prev->link,
+ args,
+ error);
+ (*link) = l;
+ }
+
+ (*error)++;
+ } else {
+ if ((*type_args = (char *) type->make_args(args))
+ == NULL)
+ (*error)++;
+ }
+ }
+}
+
+/*
+ * Allocate a new fieldtype structure, initialise it and return the
+ * struct to the caller.
+ */
+static FIELDTYPE *
+_formi_create_fieldtype(void)
+{
+ FIELDTYPE *new;
+
+ if ((new = malloc(sizeof(*new))) == NULL)
+ return NULL;
+
+ new->flags = _TYPE_NO_FLAGS;
+ new->refcount = 0;
+ new->link = NULL;
+ new->make_args = NULL;
+ new->copy_args = NULL;
+ new->free_args = NULL;
+ new->field_check = NULL;
+ new->char_check = NULL;
+ new->next_choice = NULL;
+ new->prev_choice = NULL;
+
+ return new;
+}
+
+/*
+ * Set the field type of the field to be the one given.
+ */
+int
+set_field_type(FIELD *fptr, FIELDTYPE *type, ...)
+{
+ va_list args;
+ FIELD *field;
+ int error = 0;
+
+ va_start(args, type);
+
+ field = (fptr == NULL)? &_formi_default_field : fptr;
+
+ field->type = type;
+ _formi_create_field_args(type, &field->args, &type->link, &args,
+ &error);
+ va_end(args);
+
+ if (error)
+ return E_BAD_ARGUMENT;
+
+ return E_OK;
+}
+
+/*
+ * Return the field type associated with the given field
+ */
+FIELDTYPE *
+field_type(FIELD *fptr)
+{
+ FIELD *field;
+
+ field = (fptr == NULL)? &_formi_default_field : fptr;
+
+ return field->type;
+}
+
+
+/*
+ * Return the field arguments for the given field.
+ */
+char *
+field_arg(FIELD *fptr)
+{
+ FIELD *field;
+
+ field = (fptr == NULL)? &_formi_default_field : fptr;
+
+ return field->args;
+}
+
+/*
+ * Create a new field type. Caller must specify a field_check routine
+ * and char_check routine.
+ */
+FIELDTYPE *
+new_fieldtype(int (*field_check)(FIELD *, char *),
+ int (*char_check)(int, char *))
+{
+ FIELDTYPE *new;
+
+ if ((field_check == NULL) && (char_check == NULL))
+ return NULL;
+
+ if ((new = _formi_create_fieldtype()) != NULL) {
+ new->field_check = field_check;
+ new->char_check = char_check;
+ }
+
+ return new;
+}
+
+/*
+ * Free the storage used by the fieldtype.
+ */
+int
+free_fieldtype(FIELDTYPE *fieldtype)
+{
+ if (fieldtype == NULL)
+ return E_BAD_ARGUMENT;
+
+ if (fieldtype->refcount > 0)
+ return E_CONNECTED;
+
+ if ((fieldtype->flags & _TYPE_IS_BUILTIN) == _TYPE_IS_BUILTIN)
+ return E_BAD_ARGUMENT; /* don't delete builtin types! */
+
+ if ((fieldtype->flags & _TYPE_IS_LINKED) == _TYPE_IS_LINKED)
+ {
+ fieldtype->link->next->refcount--;
+ fieldtype->link->prev->refcount--;
+ }
+
+ free(fieldtype);
+
+ return E_OK;
+}
+
+/*
+ * Set the field type arguments for the given field type.
+ */
+int
+set_fieldtype_arg(FIELDTYPE *fieldtype, char * (*make_args)(va_list *),
+ char * (*copy_args)(char*), void (*free_args)(char *))
+{
+ if ((fieldtype == NULL) || (make_args == NULL)
+ || (copy_args == NULL) || (free_args == NULL))
+ return E_BAD_ARGUMENT;
+
+ fieldtype->make_args = make_args;
+ fieldtype->copy_args = copy_args;
+ fieldtype->free_args = free_args;
+
+ return E_OK;
+}
+
+/*
+ * Set up the choice list functions for the given fieldtype.
+ */
+int
+set_fieldtype_choice(FIELDTYPE *fieldtype, int (*next_choice)(FIELD *, char *),
+ int (*prev_choice)(FIELD *, char *))
+{
+ if ((fieldtype == NULL) || (next_choice == NULL)
+ || (prev_choice == NULL))
+ return E_BAD_ARGUMENT;
+
+ fieldtype->next_choice = next_choice;
+ fieldtype->prev_choice = prev_choice;
+
+ return E_OK;
+}
+
+/*
+ * Link the two given types to produce a new type, return this new type.
+ */
+FIELDTYPE *
+link_fieldtype(FIELDTYPE *type1, FIELDTYPE *type2)
+{
+ FIELDTYPE *new;
+
+ if ((type1 == NULL) || (type2 == NULL))
+ return NULL;
+
+ if ((new = _formi_create_fieldtype()) == NULL)
+ return NULL;
+
+ new->flags = _TYPE_IS_LINKED;
+ new->flags |= ((type1->flags & _TYPE_HAS_ARGS)
+ | (type2->flags & _TYPE_HAS_ARGS));
+ if ((new->link = malloc(sizeof(*new->link))) == NULL) {
+ free(new);
+ return NULL;
+ }
+
+ new->link->prev = type1;
+ new->link->next = type2;
+ type1->refcount++;
+ type2->refcount++;
+
+ return new;
+}
--- /dev/null
+/* $NetBSD: form.c,v 1.15 2004/11/24 11:57:09 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 <sys/cdefs.h>
+__RCSID("$NetBSD: form.c,v 1.15 2004/11/24 11:57:09 blymn Exp $");
+
+#include <stdlib.h>
+#include <strings.h>
+#include <form.h>
+#include "internals.h"
+
+extern FIELD _formi_default_field;
+
+FORM _formi_default_form = {
+ FALSE, /* true if performing a init or term function */
+ FALSE, /* the form is posted */
+ FALSE, /* make field list circular if true */
+ NULL, /* window for the form */
+ NULL, /* subwindow for the form */
+ NULL, /* use this window for output */
+ NULL, /* user defined pointer */
+ 0, /* options for the form */
+ NULL, /* function called when form posted and
+ after page change */
+ NULL, /* function called when form is unposted and
+ before page change */
+ NULL, /* function called when form posted and after
+ current field changes */
+ NULL, /* function called when form unposted and
+ before current field changes */
+ 0, /* number of fields attached */
+ 0, /* current field */
+ 0, /* current page of form */
+ 0, /* number of pages in the form */
+ NULL, /* dynamic array of fields that start
+ the pages */
+ {NULL, NULL}, /* sorted field list */
+ NULL /* array of fields attached to this form. */
+};
+
+/*
+ * Set the window associated with the form
+ */
+int
+set_form_win(FORM *form, WINDOW *win)
+{
+ if (form == NULL) {
+ _formi_default_form.win = win;
+ _formi_default_form.scrwin = win;
+ } else {
+ if (form->posted == TRUE)
+ return E_POSTED;
+ else {
+ form->win = win;
+ form->scrwin = win;
+ }
+ }
+
+ return E_OK;
+}
+
+/*
+ * Return the window used by the given form
+ */
+WINDOW *
+form_win(FORM *form)
+{
+ if (form == NULL)
+ return _formi_default_form.win;
+ else
+ return form->win;
+}
+
+/*
+ * Set the subwindow for the form.
+ */
+int
+set_form_sub(FORM *form, WINDOW *window)
+{
+ if (form == NULL) {
+ _formi_default_form.subwin = window;
+ _formi_default_form.scrwin = window;
+ } else {
+ if (form->posted == TRUE)
+ return E_POSTED;
+ else {
+ form->subwin = window;
+ form->scrwin = window;
+ }
+ }
+
+ return E_OK;
+}
+
+/*
+ * Return the subwindow for the given form.
+ */
+WINDOW *
+form_sub(FORM *form)
+{
+ if (form == NULL)
+ return _formi_default_form.subwin;
+ else
+ return form->subwin;
+}
+
+/*
+ * Return the minimum size required to contain the form.
+ */
+int
+scale_form(FORM *form, int *rows, int *cols)
+{
+ int i, max_row, max_col, temp;
+
+ if ((form->fields == NULL) || (form->fields[0] == NULL))
+ return E_NOT_CONNECTED;
+
+ max_row = 0;
+ max_col = 0;
+
+ for (i = 0; i < form->field_count; i++) {
+ temp = form->fields[i]->form_row + form->fields[i]->rows;
+ max_row = (temp > max_row)? temp : max_row;
+ temp = form->fields[i]->form_col + form->fields[i]->cols;
+ max_col = (temp > max_col)? temp : max_col;
+ }
+
+ (*rows) = max_row;
+ (*cols) = max_col;
+
+ return E_OK;
+}
+
+/*
+ * Set the user defined pointer for the form given.
+ */
+int
+set_form_userptr(FORM *form, void *ptr)
+{
+ if (form == NULL)
+ _formi_default_form.userptr = ptr;
+ else
+ form->userptr = ptr;
+
+ return E_OK;
+}
+
+/*
+ * Return the user defined pointer associated with the given form.
+ */
+void *
+form_userptr(FORM *form)
+{
+
+ if (form == NULL)
+ return _formi_default_form.userptr;
+ else
+ return form->userptr;
+}
+
+/*
+ * Set the form options to the given ones.
+ */
+int
+set_form_opts(FORM *form, Form_Options options)
+{
+ if (form == NULL)
+ _formi_default_form.opts = options;
+ else
+ form->opts = options;
+
+ return E_OK;
+}
+
+/*
+ * Turn the given options on for the form.
+ */
+int
+form_opts_on(FORM *form, Form_Options options)
+{
+ if (form == NULL)
+ _formi_default_form.opts |= options;
+ else
+ form->opts |= options;
+
+ return E_OK;
+}
+
+/*
+ * Turn the given options off for the form.
+ */
+int
+form_opts_off(FORM *form, Form_Options options)
+{
+ if (form == NULL)
+ _formi_default_form.opts &= ~options;
+ else
+ form->opts &= ~options;
+
+
+ return E_OK;
+}
+
+/*
+ * Return the options set for the given form.
+ */
+Form_Options
+form_opts(FORM *form)
+{
+ if (form == NULL)
+ return _formi_default_form.opts;
+ else
+ return form->opts;
+}
+
+/*
+ * Set the form init function for the given form
+ */
+int
+set_form_init(FORM *form, Form_Hook func)
+{
+ if (form == NULL)
+ _formi_default_form.form_init = func;
+ else
+ form->form_init = func;
+
+ return E_OK;
+}
+
+/*
+ * Return the init function associated with the given form.
+ */
+Form_Hook
+form_init(FORM *form)
+{
+ if (form == NULL)
+ return _formi_default_form.form_init;
+ else
+ return form->form_init;
+}
+
+/*
+ * Set the function to be called on form termination.
+ */
+int
+set_form_term(FORM *form, Form_Hook function)
+{
+ if (form == NULL)
+ _formi_default_form.form_term = function;
+ else
+ form->form_term = function;
+
+ return E_OK;
+}
+
+/*
+ * Return the function defined for the termination function.
+ */
+Form_Hook
+form_term(FORM *form)
+{
+
+ if (form == NULL)
+ return _formi_default_form.form_term;
+ else
+ return form->form_term;
+}
+
+
+/*
+ * Attach the given fields to the form.
+ */
+int
+set_form_fields(FORM *form, FIELD **fields)
+{
+ int num_fields = 0, i, maxpg = 1, status;
+
+ if (form == NULL)
+ return E_BAD_ARGUMENT;
+
+ if (form->posted == TRUE)
+ return E_POSTED;
+
+ if (fields == NULL)
+ return E_BAD_ARGUMENT;
+
+ while (fields[num_fields] != NULL) {
+ if ((fields[num_fields]->parent != NULL) &&
+ (fields[num_fields]->parent != form))
+ return E_CONNECTED;
+ num_fields++;
+ }
+
+ /* disconnect old fields, if any */
+ if (form->fields != NULL) {
+ for (i = 0; i < form->field_count; i++) {
+ form->fields[i]->parent = NULL;
+ form->fields[i]->index = -1;
+ }
+ }
+
+ /* kill old page pointers if any */
+ if (form->page_starts != NULL)
+ free(form->page_starts);
+
+ form->field_count = num_fields;
+
+ /* now connect the new fields to the form */
+ for (i = 0; i < num_fields; i++) {
+ fields[i]->parent = form;
+ fields[i]->index = i;
+ /* set the page number of the field */
+ if (fields[i]->page_break == 1)
+ maxpg++;
+ fields[i]->page = maxpg;
+ }
+
+ form->fields = fields;
+ form->cur_field = 0;
+ form->max_page = maxpg;
+ if ((status = _formi_find_pages(form)) != E_OK)
+ return status;
+
+ /* sort the fields and set the navigation pointers */
+ _formi_sort_fields(form);
+ _formi_stitch_fields(form);
+
+ return E_OK;
+}
+
+/*
+ * Return the fields attached to the form given.
+ */
+FIELD **
+form_fields(FORM *form)
+{
+ if (form == NULL)
+ return NULL;
+
+ return form->fields;
+}
+
+/*
+ * Return the number of fields attached to the given form.
+ */
+int
+field_count(FORM *form)
+{
+ if (form == NULL)
+ return -1;
+
+ return form->field_count;
+}
+
+/*
+ * Move the given field to the row and column given.
+ */
+int
+move_field(FIELD *fptr, int frow, int fcol)
+{
+ FIELD *field = (fptr == NULL) ? &_formi_default_field : fptr;
+
+ if (field->parent != NULL)
+ return E_CONNECTED;
+
+ field->form_row = frow;
+ field->form_col = fcol;
+
+ return E_OK;
+}
+
+/*
+ * Set the page of the form to the given page.
+ */
+int
+set_form_page(FORM *form, int page)
+{
+ if (form == NULL)
+ return E_BAD_ARGUMENT;
+
+ if (form->in_init == TRUE)
+ return E_BAD_STATE;
+
+ if (page > form->max_page)
+ return E_BAD_ARGUMENT;
+
+ form->page = page;
+ return E_OK;
+}
+
+/*
+ * Return the maximum page of the form.
+ */
+int
+form_max_page(FORM *form)
+{
+ if (form == NULL)
+ return _formi_default_form.max_page;
+ else
+ return form->max_page;
+}
+
+/*
+ * Return the current page of the form.
+ */
+int
+form_page(FORM *form)
+{
+ if (form == NULL)
+ return E_BAD_ARGUMENT;
+
+ return form->page;
+}
+
+/*
+ * Set the current field to the field given.
+ */
+int
+set_current_field(FORM *form, FIELD *field)
+{
+ if (form == NULL)
+ return E_BAD_ARGUMENT;
+
+ if (form->in_init == TRUE)
+ return E_BAD_STATE;
+
+ if (field == NULL)
+ return E_INVALID_FIELD;
+
+ if ((field->parent == NULL) || (field->parent != form))
+ return E_INVALID_FIELD; /* field is not of this form */
+
+ form->cur_field = field->index;
+
+ /* XXX update page if posted??? */
+ return E_OK;
+}
+
+/*
+ * Return the current field of the given form.
+ */
+FIELD *
+current_field(FORM *form)
+{
+ if (form == NULL)
+ return NULL;
+
+ if (form->fields == NULL)
+ return NULL;
+
+ return form->fields[form->cur_field];
+}
+
+/*
+ * Allocate a new form with the given fields.
+ */
+FORM *
+new_form(FIELD **fields)
+{
+ FORM *new;
+
+ if ((new = (FORM *) malloc(sizeof(FORM))) == NULL)
+ return NULL;
+
+
+ /* copy in the defaults... */
+ bcopy(&_formi_default_form, new, sizeof(FORM));
+
+ if (new->win == NULL)
+ new->scrwin = stdscr; /* something for curses to write to */
+
+ if (fields != NULL) { /* attach the fields, if any */
+ if (set_form_fields(new, fields) < 0) {
+ free(new); /* field attach failed, back out */
+ return NULL;
+ }
+ }
+
+ return new;
+}
+
+/*
+ * Free the given form.
+ */
+int
+free_form(FORM *form)
+{
+ int i;
+
+ if (form == NULL)
+ return E_BAD_ARGUMENT;
+
+ if (form->posted == TRUE)
+ return E_POSTED;
+
+ for (i = 0; i < form->field_count; i++) {
+ /* detach all the fields from the form */
+ form->fields[i]->parent = NULL;
+ form->fields[i]->index = -1;
+ }
+
+ free(form);
+
+ return E_OK;
+}
+
+/*
+ * Tell if the current field of the form has offscreen data ahead
+ */
+int
+data_ahead(FORM *form)
+{
+ FIELD *cur;
+
+ if ((form == NULL) || (form->fields == NULL)
+ || (form->fields[0] == NULL))
+ return FALSE;
+
+ cur = form->fields[form->cur_field];
+
+ /*XXXX wrong */
+ if (cur->cur_line->expanded > cur->cols)
+ return TRUE;
+
+ return FALSE;
+}
+
+/*
+ * Tell if current field of the form has offscreen data behind
+ */
+int
+data_behind(FORM *form)
+{
+ FIELD *cur;
+
+ if ((form == NULL) || (form->fields == NULL)
+ || (form->fields[0] == NULL))
+ return FALSE;
+
+ cur = form->fields[form->cur_field];
+
+ if (cur->start_char > 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+/*
+ * Position the form cursor.
+ */
+int
+pos_form_cursor(FORM *form)
+{
+ FIELD *cur;
+ int row, col;
+
+ if ((form == NULL) || (form->fields == NULL) ||
+ (form->fields[0] == NULL))
+ return E_BAD_ARGUMENT;
+
+ if (form->posted != 1)
+ return E_NOT_POSTED;
+
+ cur = form->fields[form->cur_field];
+ row = cur->form_row;
+ col = cur->form_col;
+
+ /* if the field is public then show the cursor pos */
+ if ((cur->opts & O_PUBLIC) == O_PUBLIC) {
+ row += cur->cursor_ypos;
+ col += cur->cursor_xpos;
+ if (cur->cursor_xpos >= cur->cols) {
+ col = cur->form_col;
+ row++;
+ }
+ }
+
+#ifdef DEBUG
+ fprintf(dbg, "pos_cursor: row=%d, col=%d\n", row, col);
+#endif
+
+ wmove(form->scrwin, row, col);
+
+ return E_OK;
+}
--- /dev/null
+/* $NetBSD: form.h,v 1.21 2011/11/28 12:44:18 joerg 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.
+ *
+ *
+ */
+
+#ifndef FORM_H
+#define FORM_H 1
+#include <sys/queue.h>
+#include <stdarg.h>
+#include <curses.h>
+#include <eti.h>
+
+/* Define the types of field justification that can be used. */
+#define NO_JUSTIFICATION (0)
+#define JUSTIFY_RIGHT (1)
+#define JUSTIFY_LEFT (2)
+#define JUSTIFY_CENTER (3)
+
+/* Define the max and min valid justification styles for range checking */
+#define MIN_JUST_STYLE NO_JUSTIFICATION
+#define MAX_JUST_STYLE JUSTIFY_CENTER
+
+/* Options for the fields */
+typedef unsigned int Form_Options;
+
+/* form options */
+#define O_BS_OVERLOAD (0x001)
+#define O_NL_OVERLOAD (0x002)
+
+/* field options */
+#define O_VISIBLE (0x001) /* Field is visible */
+#define O_ACTIVE (0x002) /* Field is active in the form */
+#define O_PUBLIC (0x004) /* The contents entered into the field is echoed */
+#define O_EDIT (0x008) /* Can edit the field */
+#define O_WRAP (0x010) /* The field contents can line wrap */
+#define O_BLANK (0x020) /* Blank the field on modification */
+#define O_AUTOSKIP (0x040) /* Skip to next field when current is full */
+#define O_NULLOK (0x080) /* Field is allowed to contain no data */
+#define O_STATIC (0x100) /* Field is not dynamic */
+#define O_PASSOK (0x200) /* An umodified field is OK */
+#define O_REFORMAT (0x400) /* Insert newlines at linebreaks on buffer get */
+
+/*
+ * Form driver requests - be VERY careful about changing the ordering
+ * of the requests below. The form driver code depends on a particular
+ * order for the requests.
+ */
+#define REQ_MIN_REQUEST (KEY_MAX + 0x101) /* must equal value of the
+ first request */
+
+#define REQ_NEXT_PAGE (KEY_MAX + 0x101) /* next page in form */
+#define REQ_PREV_PAGE (KEY_MAX + 0x102) /* previous page in form */
+#define REQ_FIRST_PAGE (KEY_MAX + 0x103) /* goto first page in form */
+#define REQ_LAST_PAGE (KEY_MAX + 0x104) /* goto last page in form */
+
+#define REQ_NEXT_FIELD (KEY_MAX + 0x105) /* move to the next field */
+#define REQ_PREV_FIELD (KEY_MAX + 0x106) /* move to the previous field */
+#define REQ_FIRST_FIELD (KEY_MAX + 0x107) /* goto the first field */
+#define REQ_LAST_FIELD (KEY_MAX + 0x108) /* goto the last field */
+
+#define REQ_SNEXT_FIELD (KEY_MAX + 0x109) /* move to the next field
+ in sorted order */
+#define REQ_SPREV_FIELD (KEY_MAX + 0x10a) /* move to the prev field
+ in sorted order */
+#define REQ_SFIRST_FIELD (KEY_MAX + 0x10b) /* move to the first
+ sorted field */
+#define REQ_SLAST_FIELD (KEY_MAX + 0x10c) /* move to the last sorted
+ field */
+
+#define REQ_LEFT_FIELD (KEY_MAX + 0x10d) /* go left one field */
+#define REQ_RIGHT_FIELD (KEY_MAX + 0x10e) /* go right one field */
+#define REQ_UP_FIELD (KEY_MAX + 0x10f) /* go up one field */
+#define REQ_DOWN_FIELD (KEY_MAX + 0x110) /* go down one field */
+
+#define REQ_NEXT_CHAR (KEY_MAX + 0x111) /* move to the next char
+ in field */
+#define REQ_PREV_CHAR (KEY_MAX + 0x112) /* move to the previous
+ char in field */
+#define REQ_NEXT_LINE (KEY_MAX + 0x113) /* go to the next line in
+ the field */
+#define REQ_PREV_LINE (KEY_MAX + 0x114) /* go to the previous line
+ in the field */
+#define REQ_NEXT_WORD (KEY_MAX + 0x115) /* go to the next word in
+ the field */
+#define REQ_PREV_WORD (KEY_MAX + 0x116) /* go to the previous word
+ in the field */
+#define REQ_BEG_FIELD (KEY_MAX + 0x117) /* go to the beginning of
+ the field */
+#define REQ_END_FIELD (KEY_MAX + 0x118) /* go to the end of the field */
+#define REQ_BEG_LINE (KEY_MAX + 0x119) /* go to the beginning of
+ the line */
+#define REQ_END_LINE (KEY_MAX + 0x11a) /* go to the end of the
+ line */
+#define REQ_LEFT_CHAR (KEY_MAX + 0x11b) /* move left in the field */
+#define REQ_RIGHT_CHAR (KEY_MAX + 0x11c) /* move right in the field */
+#define REQ_UP_CHAR (KEY_MAX + 0x11d) /* move up in the field */
+#define REQ_DOWN_CHAR (KEY_MAX + 0x11e) /* move down in the field */
+
+#define REQ_NEW_LINE (KEY_MAX + 0x11f) /* insert/overlay a new line */
+#define REQ_INS_CHAR (KEY_MAX + 0x120) /* insert a blank char at
+ the cursor */
+#define REQ_INS_LINE (KEY_MAX + 0x121) /* insert a blank line at
+ the cursor */
+
+#define REQ_DEL_CHAR (KEY_MAX + 0x122) /* delete the current character */
+#define REQ_DEL_PREV (KEY_MAX + 0x123) /* delete the character
+ before the current */
+#define REQ_DEL_LINE (KEY_MAX + 0x124) /* delete the current line */
+#define REQ_DEL_WORD (KEY_MAX + 0x125) /* delete the word at the cursor */
+#define REQ_CLR_EOL (KEY_MAX + 0x126) /* clear to the end of the line */
+#define REQ_CLR_EOF (KEY_MAX + 0x127) /* clear to the end of the field */
+#define REQ_CLR_FIELD (KEY_MAX + 0x128) /* clear the field */
+
+#define REQ_OVL_MODE (KEY_MAX + 0x129) /* overlay mode */
+#define REQ_INS_MODE (KEY_MAX + 0x12a) /* insert mode */
+
+#define REQ_SCR_FLINE (KEY_MAX + 0x12b) /* scroll field forward one line */
+#define REQ_SCR_BLINE (KEY_MAX + 0x12c) /* scroll field backward
+ one line */
+#define REQ_SCR_FPAGE (KEY_MAX + 0x12d) /* scroll field forward one page */
+#define REQ_SCR_BPAGE (KEY_MAX + 0x12e) /* scroll field backward
+ one page */
+#define REQ_SCR_FHPAGE (KEY_MAX + 0x12f) /* scroll field forward
+ half a page */
+#define REQ_SCR_BHPAGE (KEY_MAX + 0x130) /* scroll field backward
+ half a page */
+
+#define REQ_SCR_FCHAR (KEY_MAX + 0x131) /* horizontal scroll
+ forward a character */
+#define REQ_SCR_BCHAR (KEY_MAX + 0x132) /* horizontal scroll
+ backward a character */
+#define REQ_SCR_HFLINE (KEY_MAX + 0x133) /* horizontal scroll
+ forward a line */
+#define REQ_SCR_HBLINE (KEY_MAX + 0x134) /* horizontal scroll
+ backward a line */
+#define REQ_SCR_HFHALF (KEY_MAX + 0x135) /* horizontal scroll
+ forward half a line */
+#define REQ_SCR_HBHALF (KEY_MAX + 0x136) /* horizontal scroll
+ backward half a line */
+
+#define REQ_VALIDATION (KEY_MAX + 0x137) /* validate the field */
+#define REQ_PREV_CHOICE (KEY_MAX + 0x138) /* display previous field choice */
+#define REQ_NEXT_CHOICE (KEY_MAX + 0x139) /* display next field choice */
+
+#define REQ_MAX_COMMAND (KEY_MAX + 0x139) /* must match the last
+ driver command */
+
+/* The following defines are for ncurses compatibility */
+#define MIN_FORM_COMMAND REQ_MIN_REQUEST
+#define MAX_FORM_COMMAND REQ_MAX_COMMAND
+
+
+typedef struct _form_string {
+ size_t allocated;
+ unsigned int length;
+ char *string;
+} FORM_STR;
+
+typedef struct _form_field FIELD;
+typedef struct _form_struct FORM;
+typedef struct _form_fieldtype FIELDTYPE;
+
+typedef struct _formi_page_struct _FORMI_PAGE_START;
+typedef struct formi_type_link_struct _FORMI_TYPE_LINK;
+typedef struct _formi_field_lines _FORMI_FIELD_LINES;
+
+
+typedef void (*Form_Hook)(FORM *);
+
+/* definition of a field in the form */
+struct _form_field {
+ unsigned int rows; /* rows in the field */
+ unsigned int cols; /* columns in the field */
+ unsigned int drows; /* dynamic rows */
+ unsigned int dcols; /* dynamic columns */
+ unsigned int max; /* maximum growth */
+ unsigned int form_row; /* starting row in the form subwindow */
+ unsigned int form_col; /* starting column in the form subwindow */
+ unsigned int nrows; /* number of off screen rows */
+ int index; /* index of this field in form fields array. */
+ int nbuf; /* number of buffers associated with this field */
+ int buf0_status; /* set to true if buffer 0 has changed. */
+ int justification; /* justification style of the field */
+ int overlay; /* set to true if field is in overlay mode */
+ _FORMI_FIELD_LINES *cur_line; /* pointer to the current line cursor
+ is on */
+ unsigned int start_char; /* starting char in string (horiz scroll) */
+ _FORMI_FIELD_LINES *start_line; /* start line in field (vert scroll) */
+ unsigned int row_count; /* number of rows actually used in field */
+ unsigned int row_xpos; /* char offset of cursor in field, not same
+ as cursor_xpos due to tab expansion */
+ unsigned int cursor_xpos; /* x pos of cursor in field */
+ unsigned int cursor_ypos; /* y pos of cursor in field */
+ short page_break; /* start of a new page on the form if 1 */
+ short page; /* number of the page this field is on */
+ chtype fore; /* character attributes for the foreground */
+ chtype back; /* character attributes for the background */
+ int pad; /* padding character */
+ Form_Options opts; /* options for the field */
+ FORM *parent; /* the form this field is bound to, if any */
+ FIELD *up; /* field above this one */
+ FIELD *down; /* field below this one */
+ FIELD *left; /* field to the left of this one */
+ FIELD *right; /* field to the right of this one */
+ void *userptr; /* user defined pointer. */
+ FIELD *link; /* used if fields are linked */
+ FIELDTYPE *type; /* type struct for the field */
+ CIRCLEQ_ENTRY(_form_field) glue; /* circle queue glue for sorting fields */
+ char *args; /* args for field type. */
+ _FORMI_FIELD_LINES *alines; /* array of the starts and ends of lines */
+ _FORMI_FIELD_LINES *free; /* list of lines available for reuse */
+ FORM_STR *buffers; /* array of buffers for the field */
+};
+
+/* define the types of fields we can have */
+extern FIELDTYPE *TYPE_ALNUM;
+extern FIELDTYPE *TYPE_ALPHA;
+extern FIELDTYPE *TYPE_ENUM;
+extern FIELDTYPE *TYPE_INTEGER;
+extern FIELDTYPE *TYPE_NUMERIC;
+extern FIELDTYPE *TYPE_REGEXP;
+extern FIELDTYPE *TYPE_IPV4;
+extern FIELDTYPE *TYPE_IPV6;
+extern FIELDTYPE *TYPE_USER;
+
+/* definition of a field type. */
+struct _form_fieldtype {
+ unsigned flags; /* status of the type */
+ unsigned refcount; /* in use if > 0 */
+ _FORMI_TYPE_LINK *link; /* set if this type is linked */
+
+ char * (*make_args)(va_list *); /* make the args for the type */
+ char * (*copy_args)(char *); /* copy the args for the type */
+ void (*free_args)(char *); /* free storage used by the args */
+ int (*field_check)(FIELD *, char *); /* field validation routine */
+ int (*char_check)(int, char *); /* char validation routine */
+ int (*next_choice)(FIELD *, char *); /* function to select next
+ choice */
+ int (*prev_choice)(FIELD *, char *); /* function to select prev
+ choice */
+};
+
+/*definition of a form */
+
+struct _form_struct {
+ int in_init; /* true if performing a init or term function */
+ int posted; /* the form is posted */
+ int wrap; /* wrap from last field to first field if true */
+ WINDOW *win; /* window for the form */
+ WINDOW *subwin; /* subwindow for the form */
+ WINDOW *scrwin; /* this is the window to use for output */
+ void *userptr; /* user defined pointer */
+ Form_Options opts; /* options for the form */
+ Form_Hook form_init; /* function called when form posted and
+ after page change */
+ Form_Hook form_term; /* function called when form is unposted and
+ before page change */
+ Form_Hook field_init; /* function called when form posted and after
+ current field changes */
+ Form_Hook field_term; /* function called when form unposted and
+ before current field changes */
+ int field_count; /* number of fields attached */
+ int cur_field; /* current field */
+ int page; /* current page of form */
+ int max_page; /* number of pages in the form */
+ _FORMI_PAGE_START *page_starts; /* dynamic array of fields that start
+ the pages */
+ CIRCLEQ_HEAD(_formi_sort_head, _form_field) sorted_fields; /* sorted field
+ list */
+ FIELD **fields; /* array of fields attached to this form. */
+};
+
+/* Public function prototypes. */
+__BEGIN_DECLS
+
+FIELD *current_field(FORM *);
+int data_ahead(FORM *);
+int data_behind(FORM *);
+FIELD *dup_field(FIELD *, int, int);
+int dynamic_field_info(FIELD *, int *, int *, int *);
+char *field_arg(FIELD *);
+chtype field_back(FIELD *);
+char *field_buffer(FIELD *, int);
+int field_count(FORM *);
+chtype field_fore(FIELD *);
+int field_index(FIELD *);
+int field_info(FIELD *, int *, int *, int *, int *, int *, int *);
+Form_Hook field_init(FORM *);
+int field_just(FIELD *);
+Form_Options field_opts(FIELD *);
+int field_opts_off(FIELD *, Form_Options);
+int field_opts_on(FIELD *, Form_Options);
+int field_pad(FIELD *);
+int field_status(FIELD *);
+Form_Hook field_term(FORM *);
+FIELDTYPE *field_type(FIELD *);
+void *field_userptr(FIELD *);
+int form_driver(FORM *, int);
+FIELD **form_fields(FORM *);
+Form_Hook form_init(FORM *);
+int form_max_page(FORM *);
+Form_Options form_opts(FORM *);
+int form_opts_off(FORM *, Form_Options);
+int form_opts_on(FORM *, Form_Options);
+int form_page(FORM *);
+WINDOW *form_sub(FORM *);
+Form_Hook form_term(FORM *);
+void *form_userptr(FORM *);
+WINDOW *form_win(FORM *);
+int free_field(FIELD *);
+int free_fieldtype(FIELDTYPE *);
+int free_form(FORM *);
+FIELD *link_field(FIELD *, int, int);
+FIELDTYPE *link_fieldtype(FIELDTYPE *, FIELDTYPE *);
+int move_field(FIELD *, int, int);
+FIELD *new_field(int, int, int, int, int, int);
+FIELDTYPE *new_fieldtype(int (* field_check)(FIELD *, char *),
+ int (* char_check)(int, char *));
+FORM *new_form(FIELD **);
+int new_page(FIELD *);
+int pos_form_cursor(FORM *);
+int post_form(FORM *);
+int scale_form(FORM *, int *, int *);
+int set_current_field(FORM *, FIELD *);
+int set_field_back(FIELD *, chtype);
+int set_field_buffer(FIELD *, int, char *);
+int set_field_fore(FIELD *, chtype);
+int set_field_init(FORM *, Form_Hook);
+int set_field_just(FIELD *, int);
+int set_field_opts(FIELD *, Form_Options);
+int set_field_pad(FIELD *, int);
+int set_field_printf(FIELD *, int, char *, ...) __printflike(3, 4);
+int set_field_status(FIELD *, int);
+int set_field_term(FORM *, Form_Hook);
+int set_field_type(FIELD *, FIELDTYPE *, ...);
+int set_field_userptr(FIELD *, void *);
+int set_fieldtype_arg(FIELDTYPE *, char *(*)(va_list *),
+ char *(*)(char *),
+ void (*)(char *));
+int set_fieldtype_choice(FIELDTYPE *, int (*)(FIELD *, char *),
+ int (*)(FIELD *, char *));
+int set_form_fields(FORM *, FIELD **);
+int set_form_init(FORM *, Form_Hook);
+int set_form_opts(FORM *, Form_Options);
+int set_form_page(FORM *, int);
+int set_form_sub(FORM *, WINDOW *);
+int set_form_term(FORM *, Form_Hook);
+int set_form_userptr(FORM *, void *);
+int set_form_win(FORM *, WINDOW *);
+int set_max_field(FIELD *, int);
+int set_new_page(FIELD *, int);
+int unpost_form(FORM *);
+
+__END_DECLS
+
+#endif /* FORM_H */
--- /dev/null
+.\" $NetBSD: form_cursor.3,v 1.9 2010/03/22 21:58:31 joerg Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd January 1, 2001
+.Dt FORMS 3
+.Os
+.Sh NAME
+.Nm pos_form_cursor
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft int
+.Fn pos_form_cursor "FORM *form"
+.Sh DESCRIPTION
+The function
+.Fn pos_form_cursor
+positions the screen cursor at the correct position for the form.
+This function can be used to restore the cursor state after using
+other curses routines.
+.Sh RETURN VALUES
+.Fn pos_form_cursor
+will return one of the following error values:
+.Pp
+.Bl -tag -width E_UNKNOWN_COMMAND -compact
+.It Er E_OK
+The function was successful.
+.It Er E_BAD_ARGUMENT
+A bad argument was passed to the function.
+.It Er E_NOT_POSTED
+The form is not posted to the screen.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
--- /dev/null
+.\" $NetBSD: form_data.3,v 1.9 2010/03/22 21:58:31 joerg Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd January 1, 2001
+.Dt FORM_DATA 3
+.Os
+.Sh NAME
+.Nm form
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft int
+.Fn data_ahead "FORM *form"
+.Ft int
+.Fn data_behind "FORM *form"
+.Sh DESCRIPTION
+If there is data offscreen to the right of the current field of the
+given form then
+.Fn data_ahead
+will return
+.Dv TRUE ,
+otherwise
+.Dv FALSE
+is returned.
+Similarly, if there is
+data offscreen to the left of the current field of the given form then
+.Fn data_behind
+will return
+.Dv TRUE .
+.Sh RETURN VALUES
+If the condition is met then the functions will return
+.Dv TRUE ,
+if there
+is an error or there is no data offscreen the functions will return
+.Dv FALSE .
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
--- /dev/null
+.\" $NetBSD: form_driver.3,v 1.11 2010/03/22 21:58:31 joerg Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd January 1, 2001
+.Dt FORMS 3
+.Os
+.Sh NAME
+.Nm form_driver
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft int
+.Fn form_driver "FORM *form" "int request"
+.Sh DESCRIPTION
+The
+.Fn form_driver
+is the heart of the forms library, it takes commands in the
+.Fa request
+parameter that is either a request to the driver to perform some
+action or is a character to be inserted into the current field.
+The form driver will attempt to insert any printable character passed to
+it into the current field.
+This may or may not succeed depending on the state of the current field.
+If the character passed is not
+printable then the driver attempts to process it as a driver request.
+If the character passed is not a valid request then the driver will
+return an unknown command error.
+.Sh PARAMETERS
+The forms driver recognizes the following requests:
+.Pp
+.Bl -tag -width REQ_SFIRST_FIELD -compact
+.It REQ_NEXT_PAGE
+Change to the next page in the form.
+.It REQ_PREV_PAGE
+Change to the previous page in the form.
+.It REQ_FIRST_PAGE
+Select the first page in the form.
+.It REQ_LAST_PAGE
+Go to the last page in the form.
+.It REQ_NEXT_FIELD
+Move to the next field in the form field array.
+.It REQ_PREV_FIELD
+Move to the previous field in the form field array.
+.It REQ_FIRST_FIELD
+Go to the first field in the form field array.
+.It REQ_LAST_FIELD
+Go to the last field in the form field array.
+.It REQ_SNEXT_FIELD
+Move to the next sorted field on the form.
+.It REQ_SPREV_FIELD
+Move to the previous sorted field on the form.
+.It REQ_SFIRST_FIELD
+Go to the first field in the sorted list.
+.It REQ_SLAST_FIELD
+Move to the last field in the sorted list.
+.It REQ_LEFT_FIELD
+Go one field to the left on the form page.
+.It REQ_RIGHT_FIELD
+Go one field to the right on the form page.
+.It REQ_UP_FIELD
+Go up one field on the form page.
+.It REQ_DOWN_FIELD
+Go down one field on the form page.
+.It REQ_NEXT_CHAR
+Move one char to the right within the field
+.It REQ_PREV_CHAR
+Move one char to the left within the current field.
+.It REQ_NEXT_LINE
+Go down one line in the current field.
+.It REQ_PREV_LINE
+Go up one line in the current field.
+.It REQ_NEXT_WORD
+Go forward one word in the current field
+.It REQ_PREV_WORD
+Go backward one word in the current field.
+.It REQ_BEG_FIELD
+Move the cursor to the beginning of the current field.
+.It REQ_END_FIELD
+Move the cursor to the end of the current field.
+.It REQ_BEG_LINE
+Move the cursor to the beginning of the line in the current field.
+.It REQ_END_LINE
+Move the cursor to the end of the line.
+.It REQ_LEFT_CHAR
+Move the cursor left one character
+.It REQ_RIGHT_CHAR
+Move the cursor right one character
+.It REQ_UP_CHAR
+Move the cursor up one line.
+.It REQ_DOWN_CHAR
+Move the cursor down one line.
+.It REQ_NEW_LINE
+Insert a new line at the current cursor position.
+.It REQ_INS_CHAR
+Insert a blank character at the current cursor position
+.It REQ_INS_LINE
+Open a blank line at the current cursor position.
+.It REQ_DEL_CHAR
+Delete the character at the current cursor position.
+.It REQ_DEL_PREV
+Delete the character to the left of the current cursor position.
+.It REQ_DEL_LINE
+Delete the current line.
+.It REQ_DEL_WORD
+Delete the word at the current cursor position.
+.It REQ_CLR_EOL
+Clear the field from the current cursor position to the end of the
+current line.
+.It REQ_CLR_EOF
+Clear the field from the current cursor position to the end of the field.
+.It REQ_CLR_FIELD
+Clear the field.
+.It REQ_OVL_MODE
+Enter overlay mode, characters added to the field will replace the
+ones already there.
+.It REQ_INS_MODE
+Enter insert mode, characters will be inserted at the current cursor
+position.
+Any characters to the right of the cursor will be moved
+right to accommodate the new characters.
+.It REQ_SCR_FLINE
+Scroll the field forward one line.
+.It REQ_SCR_BLINE
+Scroll the field backward one line.
+.It REQ_SCR_FPAGE
+Scroll the field forward one field page.
+.It REQ_SCR_BPAGE
+Scroll the field backward one field page.
+.It REQ_SCR_FHPAGE
+Scroll the field forward half one field page.
+.It REQ_SCR_BHPAGE
+Scroll the field backward half one field page.
+.It REQ_SCR_FCHAR
+Scroll the field horizontally forward one character
+.It REQ_SCR_BCHAR
+Scroll the field horizontally backward one character
+.It REQ_SCR_HFLINE
+Scroll the field horizontally forward one field line.
+.It REQ_SCR_HBLINE
+Scroll the field horizontally backward one field line.
+.It REQ_SCR_HFHALF
+Scroll the field horizontally forward half a field line.
+.It REQ_SCR_HBHALF
+Scroll the field horizontally backward half a field line.
+.It REQ_VALIDATION
+Request the contents of the current field be validated using any field
+validation function that has been set for the field.
+Normally, the field is validated before the current field changes.
+This request allows the current field to be validated.
+.It REQ_PREV_CHOICE
+Select the previous choice in an enumerated type field.
+.It REQ_NEXT_CHOICE
+Select the next choice in an enumerated type field.
+.El
+.Sh RETURN VALUES
+Functions returning pointers will return
+.Dv NULL
+if an error is detected.
+The functions that return an int will return one of the following error
+values:
+.Pp
+.Bl -tag -width E_UNKNOWN_COMMAND -compact
+.It Er E_OK
+The function was successful.
+.It Er E_REQUEST_DENIED
+The forms driver request could not be fulfilled
+.It Er E_UNKNOWN_COMMAND
+The passed character is not a printable character and is not a valid
+forms driver request.
+.It Er E_BAD_ARGUMENT
+A bad argument was passed to the forms driver.
+.It Er E_INVALID_FIELD
+The form passed to the driver has no valid attached fields.
+.It Er E_NOT_POSTED
+The given form is not currently posted to the screen.
+.It Er E_BAD_STATE
+The forms driver was called from within an init or term function.
+.It Er E_INVALID_FIELD
+The character passed to the forms driver fails the character
+validation for the current field.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+Field sorting is done by location of the field on the form page, the
+fields are sorted by position starting with the top-most, left-most
+field and progressing left to right.
+For the purposes of sorting, the
+fields top left corner is used as the sort criteria.
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
--- /dev/null
+.\" $NetBSD: form_field.3,v 1.12 2012/10/08 18:15:09 njoly Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd January 1, 2001
+.Dt FORMS 3
+.Os
+.Sh NAME
+.Nm field_count ,
+.Nm form_fields ,
+.Nm move_field ,
+.Nm set_form_fields
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft int
+.Fn field_count "FORM *form"
+.Ft FIELD **
+.Fn form_fields "FORM *form"
+.Ft int
+.Fn move_field "FIELD *field" "int frow" "int fcol"
+.Ft int
+.Fn set_form_fields "FORM *form" "FIELD **fields"
+.Sh DESCRIPTION
+The
+.Fn field_count
+function returns the number of fields that are attached to the given
+form, if the form argument passed is
+.Dv NULL
+then
+.Fn field_count
+will return \-1.
+The function
+.Fn form_fields
+will return a pointer to array of attach fields for the given form,
+this array is not
+.Dv NULL
+terminated, fields may be attached to the given
+form by calling
+.Fn set_form_fields .
+The
+.Fa fields
+argument in this function is a pointer to a
+.Dv NULL
+terminated array of
+fields that will be attached to the form.
+If there are already fields attached to the form then they will be
+detached before the new fields are attached.
+The new fields given must not be attached to any other form.
+The
+.Fn move_field
+function will move the given field to the location specified by
+.Fa frow
+and
+.Fa fcol .
+.Sh RETURN VALUES
+Functions returning pointers will return
+.Dv NULL
+if an error is detected.
+The functions that return an int will return one of the following error
+values:
+.Pp
+.Bl -tag -width E_UNKNOWN_COMMAND -compact
+.It Er E_OK
+The function was successful.
+.It Er E_CONNECTED
+The field is connected to a form.
+.It Er E_POSTED
+The form is currently posted to the screen.
+.It Er E_BAD_ARGUMENT
+The function was passed a bad argument.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
--- /dev/null
+.\" $NetBSD: form_field_attributes.3,v 1.10 2010/03/22 21:58:31 joerg Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd January 1, 2001
+.Dt FORMS 3
+.Os
+.Sh NAME
+.Nm field_back ,
+.Nm field_fore ,
+.Nm field_pad ,
+.Nm set_field_back ,
+.Nm set_field_fore ,
+.Nm set_field_pad
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft chtype
+.Fn field_back "FIELD *field"
+.Ft chtype
+.Fn field_fore "FIELD *field"
+.Ft int
+.Fn field_pad "FIELD *field"
+.Ft int
+.Fn set_field_back "FIELD *field" "chtype attribute"
+.Ft int
+.Fn set_field_fore "FIELD *field" "chtype attribute"
+.Ft int
+.Fn set_field_pad "FIELD *field" "int pad"
+.Sh DESCRIPTION
+Calling the function
+.Fn field_back
+will return the character attributes that will be applied to a field
+that is not the current field, these attributes can be set by the
+.Fn set_field_back
+function.
+The
+.Fn field_fore
+function returns the character attributes that will be used to
+indicate that a field is the currently active one on the form, this
+attribute may be set by using the
+.Fn set_field_fore
+function.
+The pad character for a field is the character that will be printed in all
+field locations not occupied with actual field contents.
+The pad character can be retrieved by calling the
+.Fn field_pad
+function, the pad character is set by using the
+.Fn set_field_pad
+function.
+.Sh RETURN VALUES
+Functions returning pointers will return
+.Dv NULL
+if an error is detected.
+The functions that return an int will return one of the following error
+values:
+.Pp
+.Bl -tag -width E_UNKNOWN_COMMAND -compact
+.It Er E_OK
+The function was successful.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
--- /dev/null
+.\" $NetBSD: form_field_buffer.3,v 1.13 2010/03/22 21:58:31 joerg Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd October 15, 2005
+.Dt FORMS 3
+.Os
+.Sh NAME
+.Nm field_buffer ,
+.Nm field_status ,
+.Nm set_field_buffer ,
+.Nm set_field_printf ,
+.Nm set_field_status ,
+.Nm set_max_field
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft char *
+.Fn field_buffer "FIELD *field" "int buffer"
+.Ft int
+.Fn field_status "FIELD *field"
+.Ft int
+.Fn set_field_buffer "FIELD *field" "int buffer" "char *value"
+.Ft int
+.Fn set_field_printf "FIELD *field" "int buffer" "char *fmt" "..."
+.Ft int
+.Fn set_field_status "FIELD *field" "int status"
+.Ft int
+.Fn set_max_field "FIELD *field" "int max"
+.Sh DESCRIPTION
+The
+.Fn field_buffer
+function returns the contents of the buffer number specified by
+.Fa buffer
+for the given field.
+If the requested buffer number exceeds the
+number of buffers attached to the field then
+.Dv NULL
+will be returned.
+If the field option
+.Dv O_REFORMAT
+is enabled on the given field then
+storage will be allocated to hold the reformatted buffer.
+This storage must be release by calling
+.Xr free 3
+when it is no longer required.
+If the
+.Dv O_REFORMAT
+field option is not set then no extra storage is allocated.
+The field buffer may be set by calling
+.Fn set_field_buffer
+which will set the given buffer number to the contents of the string
+passed.
+A buffer may also be set by calling
+.Fn set_field_printf
+which sets the buffer using the format arg
+.Fa fmt
+after being expanded using the subsequent arguments in the same manner
+as
+.Xr sprintf 3
+does.
+Calling
+.Fn field_status
+will return the status of the first buffer attached to the field.
+If the field has been modified then the function will return
+.Dv TRUE
+otherwise
+.Dv FALSE
+is returned, the status of the first buffer may be
+programmatically set by calling
+.Fn set_field_status .
+The maximum growth of a dynamic field can be set by calling
+.Fn set_max_field
+which limits the fields rows if the field is a multiline field or the
+fields columns if the field only has a single row.
+.Sh RETURN VALUES
+Functions returning pointers will return
+.Dv NULL
+if an error is detected.
+The functions that return an int will return one of the following error
+values:
+.Pp
+.Bl -tag -width E_UNKNOWN_COMMAND -compact
+.It Er E_OK
+The function was successful.
+.It Er E_BAD_ARGUMENT
+A bad parameter was passed to the function.
+.It Er E_SYSTEM_ERROR
+A system error occurred performing the function.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
+The function
+.Fn set_field_printf
+is a
+.Nx
+extension and must not be used in portable code.
--- /dev/null
+.\" $NetBSD: form_field_info.3,v 1.9 2010/03/22 21:58:31 joerg Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd January 1, 2001
+.Dt FORMS 3
+.Os
+.Sh NAME
+.Nm dynamic_field_info ,
+.Nm field_info
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft int
+.Fn dynamic_field_info "FIELD *field" "int *drows" "int *dcols" "int *max"
+.Ft int
+.Fn field_info "FIELD *field" "int *rows" "int *cols" "int *frow" "int *fcol" \
+"int *nrow" "int *nbuf"
+.Sh DESCRIPTION
+The function
+.Fn dynamic_field_info
+returns the sizing information for the field given.
+The function will return the number of rows, columns and the maximum
+growth of the field in the storage pointed to by the drows, dcols and max
+parameters respectively.
+Dynamic field information cannot be requested for the default field.
+If the field given is not dynamic then
+.Fn dynamic_field_info
+will simply return the size of the actual field.
+The
+.Fn field_info
+will return the number or rows, columns, field starting row, field
+starting column, number of off screen rows and number of buffers in
+.Fa rows ,
+.Fa cols ,
+.Fa frow ,
+.Fa fcol ,
+.Fa nrow
+and
+.Fa nbuf
+respectively.
+.Sh RETURN VALUES
+The functions will return one of the following error values:
+.Pp
+.Bl -tag -width E_UNKNOWN_COMMAND -compact
+.It Er E_OK
+The function was successful.
+.It Er E_BAD_ARGUMENT
+A bad argument was passed to the function.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
--- /dev/null
+.\" $NetBSD: form_field_just.3,v 1.11 2010/03/22 21:58:31 joerg Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd January 1, 2001
+.Dt FORMS 3
+.Os
+.Sh NAME
+.Nm field_just ,
+.Nm set_field_just
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft int
+.Fn field_just "FIELD *field"
+.Ft int
+.Fn set_field_just "FIELD *field" "int justification"
+.Sh DESCRIPTION
+Field justification is only applied to static fields, a dynamic field
+will not be justified.
+The default justification for a field is
+NO_JUSTIFICATION.
+The
+.Fn field_just
+will return the current justification value of the given field and the
+justification may be set by calling the
+.Fn set_field_just
+function.
+.Sh PARAMETERS
+The following are the valid justifications for a field:
+.Pp
+.Bl -tag -width NO_JUSTIFICATION -compact
+.It NO_JUSTIFICATION
+No justification is to be applied to the field.
+In practice, this is the same as JUSTIFY_LEFT.
+.It JUSTIFY_RIGHT
+The field will be right justified.
+That is, the end of each line will
+be butted up against the right hand side of the field.
+.It JUSTIFY_LEFT
+The field will be left justified.
+That is, the start of each line
+will be butted up against the left hand side of the field.
+.It JUSTIFY_CENTER
+The field will be centre justified, padding will be applied to either
+end of the line to make the line centred in the field.
+.El
+.Sh RETURN VALUES
+The functions will return one of the following error values:
+.Pp
+.Bl -tag -width E_UNKNOWN_COMMAND -compact
+.It Er E_OK
+The function was successful.
+.It Er E_CURRENT
+The field specified is the currently active one on the form.
+.It Er E_BAD_ARGUMENT
+A bad argument was passed to the function.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
--- /dev/null
+.\" $NetBSD: form_field_new.3,v 1.11 2010/03/22 21:58:31 joerg Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd January 1, 2001
+.Dt FORMS 3
+.Os
+.Sh NAME
+.Nm dup_field ,
+.Nm free_field ,
+.Nm link_field ,
+.Nm new_field
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft FIELD *
+.Fn dup_field "FIELD *field" "int frow" "int fcol"
+.Ft int
+.Fn free_field "FIELD *field"
+.Ft FIELD *
+.Fn link_field "FIELD *field" "int frow" "int fcol"
+.Ft FIELD *
+.Fo new_field
+.Fa "int rows"
+.Fa "int cols"
+.Fa "int frow"
+.Fa "int fcol"
+.Fa "int nrows"
+.Fa "int nbuf"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn dup_field
+function duplicates the given field, including any buffers associated
+with the field and returns the pointer to the newly created field.
+.Fn free_field
+destroys the field and frees any allocated resources associated with
+the field.
+The function
+.Fn link_field
+copies the given field to a new field at the location
+.Fa frow
+and
+.Fa fcol
+but shares the buffers with the original field.
+.Fn new_field
+creates a new field of size
+.Fa rows
+by
+.Fa cols
+at location
+.Fa frow ,
+.Fa fcol
+on the page, the argument
+.Fa nrows
+specified the number of off screen rows the field has and the
+.Fa nbuf
+parameter specifies the number of extra buffers attached to the
+field.
+There will always be one buffer associated with a field.
+.Sh RETURN VALUES
+On error
+.Fn dup_field
+and
+.Fn new_field
+will return
+.Dv NULL .
+The functions will one of the following error
+values:
+.Pp
+.Bl -tag -width E_UNKNOWN_COMMAND -compact
+.It Er E_OK
+The function was successful.
+.It Er E_BAD_ARGUMENT
+A bad argument was passed to the function.
+.It Er E_CONNECTED
+The field is connected to a form.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
--- /dev/null
+.\" $NetBSD: form_field_opts.3,v 1.11 2010/03/22 21:58:31 joerg Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd November 24, 2004
+.Dt FORMS 3
+.Os
+.Sh NAME
+.Nm field_opts ,
+.Nm field_opts_off ,
+.Nm field_opts_on ,
+.Nm set_field_opts
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft Form_Options
+.Fn field_opts "FIELD *field"
+.Ft int
+.Fn field_opts_off "FIELD *field" "Form_Options options"
+.Ft int
+.Fn field_opts_on "FIELD *field" "Form_Options options"
+.Ft int
+.Fn set_field_opts "FIELD *field" "Form_Options options"
+.Sh DESCRIPTION
+The function
+.Fn field_opts
+returns the current options settings for the given field.
+The
+.Fn field_opts_off
+will turn the options given in
+.Fa options
+off for the given field, options not specified in
+.Fa options
+will remain unchanged.
+Conversely, the function
+.Fn field_opts_on
+will turn on the options given in
+.Fa options
+for the specified field, again, any options not specified will remain
+unchanged.
+The options for a field may be set to a specific set of
+options by calling the
+.Fn set_field_opts
+function.
+Options may only be changed if the field given is not the
+currently active one.
+.Sh PARAMETERS
+The following options are available for a field:
+.Pp
+.Bl -tag -width O_REFORMAT -compact
+.It Dv O_VISIBLE
+The field is visible, hence is displayed when the form is posted.
+.It Dv O_ACTIVE
+The field is active in the form, meaning that it can be visited during
+form processing.
+.It Dv O_PUBLIC
+The contents of the field are echoed to the screen.
+.It Dv O_EDIT
+The contents of the field can be modified
+.It Dv O_WRAP
+The contents of the field are wrapped on a word boundary, if this
+option is off then the field will be wrapped on a character boundary.
+.It Dv O_BLANK
+Blank the field on new data being entered if and only if the field
+cursor is at the left hand side of the field.
+.It Dv O_AUTOSKIP
+Skip to the next field when the current field reaches its maximum
+size.
+.It Dv O_NULLOK
+The field is allowed to contain no data
+.It Dv O_STATIC
+The field is not dynamic, it has a fixed size.
+.It Dv O_PASSOK
+An unmodified field is allowed.
+.It Dv O_REFORMAT
+Retain the formatting of a field when the buffer is retrieved.
+If this option is not set then the buffer returned will be a single string
+with no line breaks.
+When this option is set newline characters will be inserted at the point
+where the string has been wrapped in a multiline field.
+This option is an extension to the forms library and must not be used
+in portable code.
+See the
+.Xr field_buffer 3
+man page for how this option modifies the behaviour of
+.Fn field_buffer .
+.El
+.Pp
+The following options are on by default for a field:
+.Dv O_VISIBLE ,
+.Dv O_ACTIVE ,
+.Dv O_PUBLIC ,
+.Dv O_EDIT ,
+.Dv O_WRAP ,
+.Dv O_BLANK ,
+.Dv O_AUTOSKIP ,
+.Dv O_NULLOK ,
+.Dv O_PASSOK ,
+and
+.Dv O_STATIC .
+.Sh RETURN VALUES
+Functions returning pointers will return
+.Dv NULL
+if an error is detected.
+The functions that return an int will return one of the following error
+values:
+.Pp
+.Bl -tag -width E_UNKNOWN_COMMAND -compact
+.It Er E_OK
+The function was successful.
+.It Er E_CURRENT
+The field specified is the currently active one in the form.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
+The option
+.Dv O_REFORMAT
+is a
+.Nx
+ extension and must not be used in portable code.
--- /dev/null
+.\" $NetBSD: form_field_userptr.3,v 1.9 2010/03/22 21:58:31 joerg Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd January 1, 2001
+.Dt FORMS 3
+.Os
+.Sh NAME
+.Nm field_userptr ,
+.Nm set_field_userptr
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft void *
+.Fn field_userptr "FIELD *field"
+.Ft int
+.Fn set_field_userptr "FIELD *field" "void *ptr"
+.Sh DESCRIPTION
+The
+.Fn field_userptr
+function returns the pointer to the user defined data for the field,
+this pointer may be set by calling the
+.Fn set_field_userptr
+function.
+.Sh RETURN VALUES
+Functions returning pointers will return
+.Dv NULL
+if an error is detected.
+The functions that return an int will return one of the following error
+values:
+.Pp
+.Bl -tag -width E_UNKNOWN_COMMAND -compact
+.It Er E_OK
+The function was successful.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
--- /dev/null
+.\" $NetBSD: form_field_validation.3,v 1.10 2010/03/22 21:58:31 joerg Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd January 1, 2001
+.Dt FORMS 3
+.Os
+.Sh NAME
+.Nm field_arg ,
+.Nm field_type ,
+.Nm set_field_type
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft char *
+.Fn field_arg "FIELD *field"
+.Ft FIELDTYPE *
+.Fn field_type "FIELD *field"
+.Ft int
+.Fn set_field_type "FIELD *field" "FIELDTYPE *type" "..."
+.Sh DESCRIPTION
+The
+.Fn field_arg
+function returns the field type arguments that are associated with the
+given field.
+The
+.Fn field_type
+function returns the field type structure associated with the given
+field, this type can be set by calling the
+.Fn set_field_type
+function which associates the given field type with the field, the
+third and subsequent parameters are field dependent arguments.
+.Sh RETURN VALUES
+Functions returning pointers will return
+.Dv NULL
+if an error is detected.
+The functions that return an int will return one of the following error
+values:
+.Pp
+.Bl -tag -width E_UNKNOWN_COMMAND -compact
+.It Er E_OK
+The function was successful.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
--- /dev/null
+.\" $NetBSD: form_fieldtype.3,v 1.11 2010/03/22 21:58:31 joerg Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd January 1, 2001
+.Dt FORMS 3
+.Os
+.Sh NAME
+.Nm free_fieldtype ,
+.Nm link_fieldtype ,
+.Nm new_fieldtype ,
+.Nm set_fieldtype_arg ,
+.Nm set_fieldtype_choice
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft int
+.Fn free_fieldtype "FIELDTYPE *fieldtype"
+.Ft FIELDTYPE *
+.Fn link_fieldtype "FIELDTYPE *type1" "FIELDTYPE *type2"
+.Ft FIELDTYPE *
+.Fo new_fieldtype
+.Fa "int (*field_check)(FIELD *, char *)"
+.Fa "int (*char_check)(int, char *)"
+.Fc
+.Ft int
+.Fo "set_fieldtype_arg"
+.Fa "FIELDTYPE *fieldtype"
+.Fa "char * (*make_args)(va_list *)"
+.Fa "char * (*copy_args)(char *)"
+.Fa "void (*free_args)(char *)"
+.Fc
+.Ft int
+.Fo set_fieldtype_choice
+.Fa "FIELDTYPE *fieldtype"
+.Fa "int (*next_choice)(FIELD *, char *)"
+.Fa "int (*prev_choice)(FIELD *, char *)"
+.Fc
+.Sh DESCRIPTION
+The function
+.Fn free_fieldtype
+frees the storage associated with the field type and destroys it.
+The function
+.Fn link_fieldtype
+links together the two given field types to produce a new field type.
+A new field type can be created by calling
+.Fn new_fieldtype
+which requires pointers to two functions which perform validation, the
+.Fa field_check
+function must validate the field contents and return
+.Dv TRUE
+if they are acceptable and
+.Dv FALSE
+if they are not.
+The
+.Fa char_check
+validates the character input into the field, this function will be
+called for each character entered, if the character can be entered
+into the field then
+.Fa char_check
+must return
+.Dv TRUE .
+Neither
+.Fa field_check
+nor
+.Fa char_check
+may be
+.Dv NULL .
+The functions for handling the field type arguments can
+be defined by using the
+.Fn set_fieldtype_arg
+function, the
+.Fa make_args
+function is used to create new arguments for the fieldtype, the
+.Fa copy_args
+is used to copy the fieldtype arguments to a new arguments structure
+and
+.Fa free_args
+is used to destroy the fieldtype arguments and release any associated
+storage, none of these function pointers may be
+.Dv NULL .
+The field type choice functions can be set by calling
+.Fn set_fieldtype_choice ,
+the
+.Fa next_choice
+and
+.Fa prev_choice
+specify the next and previous choice functions for the field type.
+These functions must perform the necessary actions to select the next
+or previous choice for the field, updating the field buffer if
+necessary.
+The choice functions must return
+.Dv TRUE
+if the function succeeded and
+.Dv FALSE
+otherwise.
+.Sh RETURN VALUES
+Functions returning pointers will return
+.Dv NULL
+if an error is detected.
+The functions that return an int will return one of the following error
+values:
+.Pp
+.Bl -tag -width E_UNKNOWN_COMMAND -compact
+.It Er E_OK
+The function was successful.
+.It Er E_BAD_ARGUMENT
+The function was passed a bad argument.
+.It Er E_CONNECTED
+The field is connected to a form.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
--- /dev/null
+.\" $NetBSD: form_hook.3,v 1.10 2010/03/22 21:58:31 joerg Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd January 1, 2001
+.Dt FORMS 3
+.Os
+.Sh NAME
+.Nm field_init ,
+.Nm field_term ,
+.Nm form_init ,
+.Nm form_term ,
+.Nm set_field_init ,
+.Nm set_field_term ,
+.Nm set_form_init ,
+.Nm set_form_term
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft void (*)(FORM *)
+.Fn field_init "FORM *form"
+.Ft void (*)(FORM *)
+.Fn field_term "FORM *form"
+.Ft void (*)(FORM *)
+.Fn form_init "FORM *form"
+.Ft void (*)(FORM *)
+.Fn form_term "FORM *form"
+.Ft int
+.Fn set_field_init "FORM *form" "void (*function)(FORM *)"
+.Ft int
+.Fn set_field_term "FORM *form" "void (*function)(FORM *)"
+.Ft int
+.Fn set_form_init "FORM *form" "void (*function)(FORM *)"
+.Ft int
+.Fn set_form_term "FORM *form" "void (*function)(FORM *)"
+.Sh DESCRIPTION
+The
+.Fn field_init
+function returns a pointer to the function that will be called just
+after the current field changes and just before the form is posted,
+this function may be set by using the
+.Fn set_field_init
+function.
+Similarly, the function
+.Fn field_term
+will return a pointer to the function that will be called just before
+the current field changes and just after the form is unposted, this
+function pointer may be set by using the
+.Fn set_field_term
+function.
+The
+.Fn form_init
+function will return a pointer to the function that will be called
+just before the form is posted to the screen, this function can be set
+by calling the
+.Fn set_form_init
+function.
+The
+.Fn form_term
+function will return a pointer to the function that will be called
+just after the form is unposted from the screen, this function may be
+set by using the
+.Fn set_form_term
+function.
+By default, the init and term function pointers are
+.Dv NULL .
+.Sh RETURN VALUES
+Functions returning pointers will return
+.Dv NULL
+if an error is detected.
+The functions that return an int will return one of the following error
+values:
+.Pp
+.Bl -tag -width E_UNKNOWN_COMMAND -compact
+.It Er E_OK
+The function was successful.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
--- /dev/null
+.\" $NetBSD: form_new.3,v 1.9 2010/03/22 21:58:31 joerg Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd January 1, 2001
+.Dt FORMS 3
+.Os
+.Sh NAME
+.Nm free_form ,
+.Nm new_form
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft int
+.Fn free_form "FORM *form"
+.Ft FORM *
+.Fn new_form "FIELD **fields"
+.Sh DESCRIPTION
+The function
+.Fn free_form
+frees all the resources associated with the form and destroys the
+form.
+Calling
+.Fn new_form
+will create a new form, set the form parameters to the current
+defaults and attach the passed fields to the form.
+The array of fields passed to
+.Fn new_form
+must be terminated with a
+.Dv NULL
+pointer to indicate the end of the fields.
+.Sh RETURN VALUES
+Functions returning pointers will return
+.Dv NULL
+if an error is detected.
+The functions that return an int will return one of the following error
+values:
+.Pp
+.Bl -tag -width E_UNKNOWN_COMMAND -compact
+.It Er E_OK
+The function was successful.
+.It Er E_BAD_ARGUMENT
+The function was passed a bad argument.
+.It Er E_POSTED
+The form is posted to the screen.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
--- /dev/null
+.\" $NetBSD: form_new_page.3,v 1.11 2010/03/22 21:58:31 joerg Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd January 1, 2001
+.Dt FORMS 3
+.Os
+.Sh NAME
+.Nm new_page ,
+.Nm set_new_page
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft int
+.Fn new_page "FIELD *field"
+.Ft int
+.Fn set_new_page "FIELD *field" "int page"
+.Sh DESCRIPTION
+The
+.Fn new_page
+function returns
+.Dv TRUE
+if the given field is the start of a new page, otherwise it returns
+.Dv FALSE ,
+the new page status of a field can be set or unset using the
+.Fn set_new_page
+function.
+.Sh RETURN VALUES
+The functions will return one of the following error values:
+.Pp
+.Bl -tag -width E_UNKNOWN_COMMAND -compact
+.It Er E_OK
+The function was successful.
+.It Er E_CONNECTED
+The field is connected to a form.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
--- /dev/null
+.\" $NetBSD: form_opts.3,v 1.10 2010/03/22 21:58:31 joerg Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd January 1, 2001
+.Dt FORMS 3
+.Os
+.Sh NAME
+.Nm form_opts ,
+.Nm form_opts_off ,
+.Nm form_opts_on ,
+.Nm set_form_opts
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft Form_Options
+.Fn form_opts "FORM *form"
+.Ft int
+.Fn form_opts_off "FORM *form" "Form_Options options"
+.Ft int
+.Fn form_opts_on "FORM *form" "Form_Options options"
+.Ft int
+.Fn set_form_opts "FORM *form" "Form_Options options"
+.Sh DESCRIPTION
+The function
+.Fn form_opts
+returns the current options that are set on the given form.
+The
+.Fn form_opts_off
+will turn off the form options given in
+.Fa options
+for the form, similarly,
+.Fn form_opts_on
+will turn on the options specified in
+.Fa options
+for the given form.
+The form options can be set to an explicit set by calling
+.Fn set_form_opts .
+.Sh PARAMETERS
+The following form options are valid:
+.Pp
+.Bl -tag -width O_BS_OVERLOAD -compact
+.It O_BS_OVERLOAD
+If this option is set and the cursor is at the first character in the
+field then the backspace character will perform the same function as a
+REQ_PREV_FIELD driver request, moving to the previous field in the
+form.
+.It O_NL_OVERLOAD
+If this option is set and the cursor is at the end of the field then
+the new line character will perform the same function as a
+REQ_NEXT_FIELD driver request, moving to the next field in the form.
+.El
+.Pp
+By default no form options are set.
+.Sh RETURN VALUES
+Functions returning pointers will return
+.Dv NULL
+if an error is detected.
+The functions that return an int will return one of the following error
+values:
+.Pp
+.Bl -tag -width E_UNKNOWN_COMMAND -compact
+.It Er E_OK
+The function was successful.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
--- /dev/null
+.\" $NetBSD: form_page.3,v 1.10 2010/03/22 21:58:31 joerg Exp $
+.\"
+.\" Copyright (c) 1999
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd January 1, 2001
+.Dt FORM_PAGE 3
+.Os
+.Sh NAME
+.Nm current_field ,
+.Nm field_index ,
+.Nm form_page ,
+.Nm form_max_page ,
+.Nm set_current_field ,
+.Nm set_form_page
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft FIELD *
+.Fn current_field "FORM *form"
+.Ft int
+.Fn field_index "FIELD *field"
+.Ft int
+.Fn form_page "FORM *form"
+.Ft int
+.Fn form_max_page "FORM *form"
+.Ft int
+.Fn set_current_field "FORM *form" "FIELD *field"
+.Ft int
+.Fn set_form_page "FORM *form" "int page"
+.Sh DESCRIPTION
+The
+.Fn current_field
+returns a pointer to the structure for the field that is currently
+active on the page.
+If there is an error,
+.Fn current_field
+will return
+.Dv NULL .
+Calling
+.Fn field_index
+will return the index of the given field in the form field array.
+The
+current page the form is on can be determined by using
+.Fn form_page ,
+the current page of a form can be programmatically set by calling
+.Fn set_form_page .
+The maximum page number for a form can be found by
+calling the function
+.Fn form_max_page
+but note that this function is a
+.Nx
+extension and must not be used in portable forms library programs.
+The current field on the form may be set by calling
+.Fn set_current_field
+which will set the current field to the one given.
+.Sh RETURN VALUES
+Functions returning pointers will return
+.Dv NULL
+if an error is detected.
+The functions that return an int will return one of the following
+error values:
+.Pp
+.Bl -tag -width E_UNKNOWN_COMMAND -compact
+.It Er E_OK
+The function was successful.
+.It Er E_BAD_ARGUMENT
+The function was passed a bad argument.
+.It Er E_NOT_CONNECTED
+The given field is not associated with a form.
+.It Er E_BAD_STATE
+The function was called from within an init or term function.
+.It Er E_INVALID_FIELD
+The field given is not part of the given form.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
+.Pp
+The
+.Nm form_max_page
+is a
+.Nx
+extension and should not be used in portable applications.
--- /dev/null
+.\" $NetBSD: form_post.3,v 1.10 2010/03/22 21:58:31 joerg Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd January 1, 2001
+.Dt FORMS 3
+.Os
+.Sh NAME
+.Nm post_form ,
+.Nm unpost_form
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft int
+.Fn post_form "FORM *form"
+.Ft int
+.Fn unpost_form "FORM *form"
+.Sh DESCRIPTION
+The
+.Fn post_form
+function performs the actions necessary to present the form on the
+curses screen.
+If there are any init functions that need to be called
+then they will be called prior to the form being posted and the cursor
+will be positioned on the first active field that can be visited.
+Conversely, the function
+.Fn unpost_form
+removes the form from the screen and calls any termination functions
+that were specified.
+.Sh RETURN VALUES
+The functions will return one of the following error values:
+.Pp
+.Bl -tag -width E_UNKNOWN_COMMAND -compact
+.It Er E_OK
+The function was successful.
+.It Er E_BAD_ARGUMENT
+A bad argument was passed to the function.
+.It Er E_POSTED
+The form is already posted to the screen.
+.It Er E_NOT_POSTED
+The form was not posted to the screen.
+.It Er E_NOT_CONNECTED
+There are no fields associated with the form.
+.It Er E_BAD_STATE
+The function was called from within a init or term function.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
--- /dev/null
+.\" $NetBSD: form_userptr.3,v 1.9 2010/03/22 21:58:31 joerg Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd January 1, 2001
+.Dt FORMS 3
+.Os
+.Sh NAME
+.Nm form_userptr ,
+.Nm set_form_userptr
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft void *
+.Fn form_userptr "FORM *form"
+.Ft int
+.Fn set_form_userptr "FORM *form" "void *ptr"
+.Sh DESCRIPTION
+The
+.Fn form_userptr
+function returns the pointer to the user defined data associated with
+the form, this pointer may be set using the
+.Fn set_form_userptr
+call.
+.Sh RETURN VALUES
+Functions returning pointers will return
+.Dv NULL
+if an error is detected.
+The functions that return an int will return one of the following error
+values:
+.Pp
+.Bl -tag -width E_UNKNOWN_COMMAND -compact
+.It Er E_OK
+The function was successful.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
--- /dev/null
+.\" $NetBSD: form_win.3,v 1.10 2010/03/22 21:58:31 joerg Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd January 1, 2001
+.Dt FORMS 3
+.Os
+.Sh NAME
+.Nm form_sub ,
+.Nm form_win ,
+.Nm scale_form ,
+.Nm set_form_sub ,
+.Nm set_form_win
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Ft WINDOW *
+.Fn form_sub "FORM *form"
+.Ft WINDOW *
+.Fn form_win "FORM *form"
+.Ft int
+.Fn scale_form "FORM *form" "int *rows" "int *cols"
+.Ft int
+.Fn set_form_sub "FORM *form" "WINDOW *window"
+.Ft int
+.Fn set_form_win "FORM *form" "WINDOW *window"
+.Sh DESCRIPTION
+All output to the screen done by the forms library is handled by the
+curses library routines.
+By default, the forms library will output to the curses
+.Fa stdscr ,
+but if the forms window has been set via
+.Fn set_form_win
+then output will be sent to the window specified by
+.Fn set_form_win ,
+unless the forms subwindow has been set using
+.Fn set_form_sub .
+If a subwindow has been specified using
+.Fn set_form_sub
+then it will be used by the forms library to for screen output.
+The current setting for the form window can be retrieved by calling
+.Fn form_win .
+If the forms window has not been set then
+.Dv NULL
+will be returned.
+Similarly, the forms subwindow can be found by calling the
+.Fn form_sub
+function, again, if the subwindow has not been set then
+.Dv NULL
+will be
+returned.
+The
+.Fn scale_form
+function will return the minimum number of rows and columns that will
+entirely contain the given form.
+.Sh RETURN VALUES
+Functions returning pointers will return
+.Dv NULL
+if an error is detected.
+The functions that return an int will return one of the following error
+values:
+.Pp
+.Bl -tag -width E_UNKNOWN_COMMAND -compact
+.It Er E_OK
+The function was successful.
+.It Er E_NOT_CONNECTED
+The form has no fields connected to it.
+.It Er E_POSTED
+The form is posted to the screen.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr forms 3
+.Sh NOTES
+The header
+.In form.h
+automatically includes both
+.In curses.h
+and
+.In eti.h .
--- /dev/null
+.\" $NetBSD: forms.3,v 1.16 2004/11/24 12:09:13 wiz Exp $
+.\"
+.\" Copyright (c) 2001
+.\" Brett Lymn - blymn@baea.com.au, brett_lymn@yahoo.com.au
+.\"
+.\" This code is 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. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.Dd November 24, 2004
+.Dt FORMS 3
+.Os
+.Sh NAME
+.Nm form
+.Nd form library
+.Sh LIBRARY
+.Lb libform
+.Sh SYNOPSIS
+.In form.h
+.Sh DESCRIPTION
+The
+.Nm
+library provides a terminal independent form system using the
+.Xr curses 3
+library.
+Before using the
+.Nm
+functions the terminal must be set up by
+.Xr curses 3
+using the
+.Fn initscr
+function or similar.
+Programs using
+.Nm
+functions must be linked with the
+.Xr curses 3
+library
+.Fl lcurses .
+.Pp
+The
+.Nm
+library provides facilities for defining form fields, placing a form on the
+terminal screen, assign pre and post change operations and setting the
+attributes of both the form and its fields.
+.Ss Defining default attributes for forms and fields
+The
+.Nm
+library allows any settable attribute or option of both the form and field
+objects to be defined such that any new form or field automatically inherits
+the value as default.
+Setting the default value will not affect any field or
+form that has already been created but will be applied to subsequent objects.
+To set the default attribute or option the set routine is passed a
+.Dv NULL
+pointer in the field or form parameter when calling the set routine.
+The current default value can be retrieved by calling the get routine with a
+.Dv NULL
+pointer for the field or form parameter.
+.Pp
+.Bl -column set_fieldtype_choiceXX
+.It Sy "Form Routine Name" Ta Sy "Manual Page Name"
+.It current_field Ta Xr form_page 3
+.It data_ahead Ta Xr form_data 3
+.It data_behind Ta Xr form_data 3
+.It dup_field Ta Xr form_field_new 3
+.It dynamic_field_info Ta Xr form_field_info 3
+.It field_arg Ta Xr form_field_validation 3
+.It field_back Ta Xr form_field_attributes 3
+.It field_buffer Ta Xr form_field_buffer 3
+.It field_count Ta Xr form_field 3
+.It field_fore Ta Xr form_field_attributes 3
+.It field_index Ta Xr form_page 3
+.It field_info Ta Xr form_field_info 3
+.It field_init Ta Xr form_hook 3
+.It field_just Ta Xr form_field_just 3
+.It field_opts Ta Xr form_field_opts 3
+.It field_opts_off Ta Xr form_field_opts 3
+.It field_opts_on Ta Xr form_field_opts 3
+.It field_pad Ta Xr form_field_attributes 3
+.It field_status Ta Xr form_field_buffer 3
+.It field_term Ta Xr form_hook 3
+.It field_type Ta Xr form_field_validation 3
+.It field_userptr Ta Xr form_field_userptr 3
+.It form_driver Ta Xr form_driver 3
+.It form_fields Ta Xr form_field 3
+.It form_init Ta Xr form_hook 3
+.It form_max_page Ta Xr form_page 3
+.It form_opts Ta Xr form_opts 3
+.It form_opts_off Ta Xr form_opts 3
+.It form_opts_on Ta Xr form_opts 3
+.It form_page Ta Xr form_page 3
+.It form_sub Ta Xr form_win 3
+.It form_term Ta Xr form_hook 3
+.It form_userptr Ta Xr form_userptr 3
+.It form_win Ta Xr form_win 3
+.It free_field Ta Xr form_field_new 3
+.It free_fieldtype Ta Xr form_fieldtype 3
+.It free_form Ta Xr form_new 3
+.It link_field Ta Xr form_field_new 3
+.It link_fieldtype Ta Xr form_fieldtype 3
+.It move_field Ta Xr form_field 3
+.It new_field Ta Xr form_field_new 3
+.It new_fieldtype Ta Xr form_fieldtype 3
+.It new_form Ta Xr form_new 3
+.It new_page Ta Xr form_new_page 3
+.It pos_form_cursor Ta Xr form_cursor 3
+.It post_form Ta Xr form_post 3
+.It scale_form Ta Xr form_win 3
+.It set_current_field Ta Xr form_page 3
+.It set_field_back Ta Xr form_field_attributes 3
+.It set_field_buffer Ta Xr form_field_buffer 3
+.It set_field_fore Ta Xr form_field_attributes 3
+.It set_field_init Ta Xr form_hook 3
+.It set_field_just Ta Xr form_field_just 3
+.It set_field_opts Ta Xr form_field_opts 3
+.It set_field_pad Ta Xr form_field_attributes 3
+.It set_field_printf Ta Xr form_field_buffer 3
+.It set_field_status Ta Xr form_field_buffer 3
+.It set_field_term Ta Xr form_hook 3
+.It set_field_type Ta Xr form_field_validation 3
+.It set_field_userptr Ta Xr form_field_userptr 3
+.It set_fieldtype_arg Ta Xr form_fieldtype 3
+.It set_fieldtype_choice Ta Xr form_fieldtype 3
+.It set_form_fields Ta Xr form_field 3
+.It set_form_init Ta Xr form_hook 3
+.It set_form_opts Ta Xr form_opts 3
+.It set_form_page Ta Xr form_page 3
+.It set_form_sub Ta Xr form_win 3
+.It set_form_term Ta Xr form_hook 3
+.It set_form_userptr Ta Xr form_userptr 3
+.It set_form_win Ta Xr form_win 3
+.It set_max_field Ta Xr form_field_buffer 3
+.It set_new_page Ta Xr form_new_page 3
+.It unpost_form Ta Xr form_post 3
+.El
+.Sh RETURN VALUES
+Any function returning a string pointer will return
+.Dv NULL
+if an error occurs.
+Functions returning an integer will return one of the following:
+.Bl -column set_fieldtype_choiceXX
+.It Dv E_OK Ta No The function was successful.
+.It Dv E_SYSTEM_ERROR Ta No There was a system error during the call.
+.It Dv E_BAD_ARGUMENT Ta No One or more of the arguments passed to \
+the function was incorrect.
+.It Dv E_POSTED Ta No The form is already posted.
+.It Dv E_CONNECTED Ta No A field was already connected to a form.
+.It Dv E_BAD_STATE Ta No The function was called from within an \
+initialization or termination routine.
+.It Dv E_NO_ROOM Ta No The form does not fit within the subwindow.
+.It Dv E_NOT_POSTED Ta No The form is not posted.
+.It Dv E_UNKNOWN_COMMAND Ta No The form driver does not recognize the \
+request passed to it.
+.It Dv E_NOT_SELECTABLE Ta No The field could not be selected.
+.It Dv E_NOT_CONNECTED Ta No The field is not connected to a form.
+.It Dv E_REQUEST_DENIED Ta No The form driver could not process the request.
+.It Dv E_INVALID_FIELD Ta No The field is invalid.
+.It Dv E_CURRENT Ta No The field is the active one on the form.
+.El
+.Sh SEE ALSO
+.Xr curses 3 ,
+.Xr menus 3
+.Sh NOTES
+This implementation of the forms library does depart in behavior
+subtly from the original AT\*[Am]T implementation.
+Some of the more notable departures are:
+.Pp
+.Bl -tag -width "The TAB character" -compact
+.It field wrapping
+For multi-line fields the data will be wrapped as it is entered, this
+does not happen in the AT\*[Am]T implementation.
+.It buffer 0
+In this implementation, the contents of buffer 0 are always current
+regardless of whether the field has been validated or not.
+.It circular fields
+In the AT\*[Am]T implementation fields are circular on a page, that is, a
+next field from the last field will go to the first field on the
+current page.
+In this implementation a next field request on the last
+field of a page will result in the forms library positioning the
+cursor on the first field of the next page.
+If the field is the last
+field in the form then going to the next field will be denied, in the
+AT\*[Am]T it would result in the cursor being placed on the first field of
+the first page.
+.It buffer returns
+In this implementation only the data entered by the user in the form
+field will be returned, unlike the AT\*[Am]T library which would return the
+contents of the field padded to the size of the field with the pad
+character.
+.It The TAB character
+The handling of the TAB character in fields varies between
+implementations.
+In ncurses attempting to set a field contents with a
+string containing a TAB will result in an error and will not allow a
+TAB to be entered into a field.
+The AT\*[Am]T library statically
+converts tabs to the equivalent number of spaces when the field buffer
+is set but the form driver will not allow a TAB to be inserted into
+the field buffer.
+This implementation allows TAB when setting the
+field buffer and also will allow TAB to be inserted into a field
+buffer via the form driver and correctly calculates the cursor
+position allowing for expansion of the TAB character.
+.It set_field_printf
+This function is a
+.Nx
+extension and must not be used in portable code.
+.It Dv O_REFORMAT
+This field option is a
+.Nx
+extension and must not be used in portable code.
+.El
--- /dev/null
+#
+# Print out the line structs
+#
+define lstructs
+ set $lstruct=(_FORMI_FIELD_LINES *)$arg0
+ while ($lstruct)
+ print *($lstruct)
+ if ($lstruct->prev != 0x0)
+ if ($lstruct->prev->next != $lstruct)
+ print "WARNING: backward pointers inconsistent"
+ end
+ end
+ if ($lstruct->next != 0x0)
+ if ($lstruct->next->prev != $lstruct)
+ print "WARNING: forward pointers inconsistent"
+ end
+ end
+ set $lstruct = $lstruct->next
+ end
+end
--- /dev/null
+/* $NetBSD: internals.c,v 1.35 2011/05/23 20:43:02 joerg 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: internals.c,v 1.35 2011/05/23 20:43:02 joerg Exp $");
+
+#include <limits.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <assert.h>
+#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;
+
+/*
+ * map the request numbers to strings for debug
+ */
+char *reqs[] = {
+ "NEXT_PAGE", "PREV_PAGE", "FIRST_PAGE", "LAST_PAGE", "NEXT_FIELD",
+ "PREV_FIELD", "FIRST_FIELD", "LAST_FIELD", "SNEXT_FIELD",
+ "SPREV_FIELD", "SFIRST_FIELD", "SLAST_FIELD", "LEFT_FIELD",
+ "RIGHT_FIELD", "UP_FIELD", "DOWN_FIELD", "NEXT_CHAR", "PREV_CHAR",
+ "NEXT_LINE", "PREV_LINE", "NEXT_WORD", "PREV_WORD", "BEG_FIELD",
+ "END_FIELD", "BEG_LINE", "END_LINE", "LEFT_CHAR", "RIGHT_CHAR",
+ "UP_CHAR", "DOWN_CHAR", "NEW_LINE", "INS_CHAR", "INS_LINE",
+ "DEL_CHAR", "DEL_PREV", "DEL_LINE", "DEL_WORD", "CLR_EOL",
+ "CLR_EOF", "CLR_FIELD", "OVL_MODE", "INS_MODE", "SCR_FLINE",
+ "SCR_BLINE", "SCR_FPAGE", "SCR_BPAGE", "SCR_FHPAGE", "SCR_BHPAGE",
+ "SCR_FCHAR", "SCR_BCHAR", "SCR_HFLINE", "SCR_HBLINE", "SCR_HFHALF",
+ "SCR_HBHALF", "VALIDATION", "PREV_CHOICE", "NEXT_CHOICE" };
+#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 */
+
+/* used in add_char for initial memory allocation for string in row */
+#define INITIAL_LINE_ALLOC 16
+
+unsigned
+field_skip_blanks(unsigned int start, _FORMI_FIELD_LINES **rowp);
+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, _FORMI_FIELD_LINES **rowp, int direction);
+void
+_formi_hscroll_back(FIELD *field, _FORMI_FIELD_LINES *row, unsigned int amt);
+void
+_formi_hscroll_fwd(FIELD *field, _FORMI_FIELD_LINES *row, 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
+_formi_set_cursor_xpos(FIELD *field, int no_scroll);
+static int
+find_sow(unsigned int offset, _FORMI_FIELD_LINES **rowp);
+static int
+split_line(FIELD *field, bool hard_split, unsigned pos,
+ _FORMI_FIELD_LINES **rowp);
+static bool
+check_field_size(FIELD *field);
+static int
+add_tab(FORM *form, _FORMI_FIELD_LINES *row, unsigned int i, char c);
+static int
+tab_size(_FORMI_FIELD_LINES *row, unsigned int i);
+static unsigned int
+tab_fit_len(_FORMI_FIELD_LINES *row, unsigned int len);
+static int
+tab_fit_window(FIELD *field, unsigned int pos, unsigned int window);
+static void
+add_to_free(FIELD *field, _FORMI_FIELD_LINES *line);
+static void
+adjust_ypos(FIELD *field, _FORMI_FIELD_LINES *line);
+static _FORMI_FIELD_LINES *
+copy_row(_FORMI_FIELD_LINES *row);
+static void
+destroy_row_list(_FORMI_FIELD_LINES *start);
+
+/*
+ * Calculate the cursor y position to make the given row appear on the
+ * field. This may be as simple as just changing the ypos (if at all) but
+ * may encompass resetting the start_line of the field to place the line
+ * at the bottom of the field. The field is assumed to be a multi-line one.
+ */
+static void
+adjust_ypos(FIELD *field, _FORMI_FIELD_LINES *line)
+{
+ unsigned ypos;
+ _FORMI_FIELD_LINES *rs;
+
+ ypos = 0;
+ rs = field->alines;
+ while (rs != line) {
+ rs = rs->next;
+ ypos++;
+ }
+
+ field->cursor_ypos = ypos;
+ field->start_line = field->alines;
+ if (ypos > (field->rows - 1)) {
+ /*
+ * cur_line off the end of the field,
+ * adjust start_line so fix this.
+ */
+ field->cursor_ypos = field->rows - 1;
+ ypos = ypos - (field->rows - 1);
+ while (ypos > 0) {
+ ypos--;
+ field->start_line = field->start_line->next;
+ }
+ }
+}
+
+
+/*
+ * Delete the given row and add it to the free list of the given field.
+ */
+static void
+add_to_free(FIELD *field, _FORMI_FIELD_LINES *line)
+{
+ _FORMI_FIELD_LINES *saved;
+
+ saved = line;
+
+ /* don't remove if only one line... */
+ if ((line->prev == NULL) && (line->next == NULL))
+ return;
+
+ if (line->prev == NULL) {
+ /* handle top of list */
+ field->alines = line->next;
+ field->alines->prev = NULL;
+
+ if (field->cur_line == saved)
+ field->cur_line = field->alines;
+ if (field->start_line == saved)
+ field->start_line = saved;
+ } else if (line->next == NULL) {
+ /* handle bottom of list */
+ line->prev->next = NULL;
+ if (field->cur_line == saved)
+ field->cur_line = saved->prev;
+ if (field->start_line == saved)
+ field->cur_line = saved->prev;
+ } else {
+ saved->next->prev = saved->prev;
+ saved->prev->next = saved->next;
+ if (field->cur_line == saved)
+ field->cur_line = saved->prev;
+ if (field->start_line == saved)
+ field->start_line = saved;
+ }
+
+ saved->next = field->free;
+ field->free = saved;
+ saved->prev = NULL;
+ if (saved->next != NULL)
+ saved->next->prev = line;
+}
+
+/*
+ * Duplicate the given row, return the pointer to the new copy or
+ * NULL if the copy fails.
+ */
+static _FORMI_FIELD_LINES *
+copy_row(_FORMI_FIELD_LINES *row)
+{
+ _FORMI_FIELD_LINES *new;
+ _formi_tab_t *tp, *newt;
+
+ if ((new = (_FORMI_FIELD_LINES *) malloc(sizeof(_FORMI_FIELD_LINES)))
+ == NULL) {
+ return NULL;
+ }
+
+ memcpy(new, row, sizeof(_FORMI_FIELD_LINES));
+
+ /* nuke the pointers from the source row so we don't get confused */
+ new->next = NULL;
+ new->prev = NULL;
+ new->tabs = NULL;
+
+ if ((new->string = (char *) malloc((size_t)new->allocated)) == NULL) {
+ free(new);
+ return NULL;
+ }
+
+ memcpy(new->string, row->string, (size_t) row->length + 1);
+
+ if (row->tabs != NULL) {
+ tp = row->tabs;
+ if ((new->tabs = (_formi_tab_t *) malloc(sizeof(_formi_tab_t)))
+ == NULL) {
+ free(new->string);
+ free(new);
+ return NULL;
+ }
+
+ memcpy(new->tabs, row->tabs, sizeof(_formi_tab_t));
+ new->tabs->back = NULL;
+ new->tabs->fwd = NULL;
+
+ tp = tp->fwd;
+ newt = new->tabs;
+
+ while (tp != NULL) {
+ if ((newt->fwd =
+ (_formi_tab_t *) malloc(sizeof(_formi_tab_t)))
+ == NULL) {
+ /* error... unwind allocations */
+ tp = new->tabs;
+ while (tp != NULL) {
+ newt = tp->fwd;
+ free(tp);
+ tp = newt;
+ }
+
+ free(new->string);
+ free(new);
+ return NULL;
+ }
+
+ memcpy(newt->fwd, tp, sizeof(_formi_tab_t));
+ newt->fwd->back = newt;
+ newt = newt->fwd;
+ newt->fwd = NULL;
+ tp = tp->fwd;
+ }
+ }
+
+ return new;
+}
+
+/*
+ * Initialise the row offset for a field, depending on the type of
+ * field it is and the type of justification used. The justification
+ * is only used on static single line fields, everything else will
+ * have the cursor_xpos set to 0.
+ */
+void
+_formi_init_field_xpos(FIELD *field)
+{
+ /* not static or is multi-line which are not justified, so 0 it is */
+ if (((field->opts & O_STATIC) != O_STATIC) ||
+ ((field->rows + field->nrows) != 1)) {
+ field->cursor_xpos = 0;
+ return;
+ }
+
+ switch (field->justification) {
+ case JUSTIFY_RIGHT:
+ field->cursor_xpos = field->cols - 1;
+ break;
+
+ case JUSTIFY_CENTER:
+ field->cursor_xpos = (field->cols - 1) / 2;
+ break;
+
+ default: /* assume left justify */
+ field->cursor_xpos = 0;
+ break;
+ }
+}
+
+
+/*
+ * 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
+
+/*
+ * 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->alines->length < field->max);
+ } else {
+ return (field->row_count <= field->max);
+ }
+ } else {
+ if ((field->rows + field->nrows) == 1) {
+ return (field->alines->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;
+}
+
+/*
+ * Destroy the list of line structs passed by freeing all allocated
+ * memory.
+ */
+static void
+destroy_row_list(_FORMI_FIELD_LINES *start)
+{
+ _FORMI_FIELD_LINES *temp, *row;
+ _formi_tab_t *tt, *tp;
+
+ row = start;
+ while (row != NULL) {
+ if (row->tabs != NULL) {
+ /* free up the tab linked list... */
+ tp = row->tabs;
+ while (tp != NULL) {
+ tt = tp->fwd;
+ free(tp);
+ tp = tt;
+ }
+ }
+
+ if (row->string != NULL)
+ free(row->string);
+
+ temp = row->next;
+ free(row);
+ row = temp;
+ }
+}
+
+/*
+ * 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, _FORMI_FIELD_LINES *loc)
+{
+ int width, wrap_err;
+ unsigned int pos, saved_xpos, saved_ypos, saved_cur_xpos;
+ unsigned int saved_row_count;
+ _FORMI_FIELD_LINES *saved_row, *row, *row_backup, *saved_cur_line;
+ _FORMI_FIELD_LINES *saved_start_line, *temp;
+
+ 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;
+ }
+ }
+
+ row = 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 (row->prev != NULL)
+ row = row->prev;
+
+ saved_row = row;
+ saved_xpos = field->row_xpos;
+ saved_cur_xpos = field->cursor_xpos;
+ saved_ypos = field->cursor_ypos;
+ saved_row_count = field->row_count;
+
+ /*
+ * Save a copy of the lines affected, just in case things
+ * don't work out.
+ */
+ if ((row_backup = copy_row(row)) == NULL)
+ return E_SYSTEM_ERROR;
+
+ temp = row_backup;
+ row = row->next;
+
+ saved_cur_line = temp;
+ saved_start_line = temp;
+
+ while (row != NULL) {
+ if ((temp->next = copy_row(row)) == NULL) {
+ /* a row copy failed... free up allocations */
+ destroy_row_list(row_backup);
+ return E_SYSTEM_ERROR;
+ }
+
+ temp->next->prev = temp;
+ temp = temp->next;
+
+ if (row == field->start_line)
+ saved_start_line = temp;
+ if (row == field->cur_line)
+ saved_cur_line = temp;
+
+ row = row->next;
+ }
+
+ row = saved_row;
+ while (row != NULL) {
+ pos = row->length - 1;
+ if (row->expanded < width) {
+ /* line may be too short, try joining some lines */
+ if ((row->hard_ret == TRUE) && (row->next != NULL)) {
+ /*
+ * Skip the line if it has a hard return
+ * and it is not the last, we cannot join
+ * anything to it.
+ */
+ row = row->next;
+ continue;
+ }
+
+ if (row->next == NULL) {
+ /*
+ * If there are no more lines and this line
+ * is too short then our job is over.
+ */
+ break;
+ }
+
+ if (_formi_join_line(field, &row,
+ JOIN_NEXT_NW) == E_OK) {
+ continue;
+ } else
+ break;
+ } else if (row->expanded > width) {
+ /* line is too long, split it */
+
+ /*
+ * split on first whitespace before current word
+ * if the line has tabs we need to work out where
+ * the field border lies when the tabs are expanded.
+ */
+ if (row->tabs == NULL) {
+ pos = width - 1;
+ if (pos >= row->expanded)
+ pos = row->expanded - 1;
+ } else {
+ pos = tab_fit_len(row, field->cols);
+ }
+
+ if ((!isblank((unsigned char)row->string[pos])) &&
+ ((field->opts & O_WRAP) == O_WRAP)) {
+ if (!isblank((unsigned char)row->string[pos - 1]))
+ pos = find_sow((unsigned int) pos,
+ &row);
+ /*
+ * 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((unsigned char)row->string[pos - 1]))) {
+ wrap_err = E_NO_ROOM;
+ goto restore_and_exit;
+ }
+ }
+
+ /* if we are at the end of the string and it has
+ * a trailing blank, don't wrap the blank.
+ */
+ if ((row->next == NULL) && (pos == row->length - 1) &&
+ (isblank((unsigned char)row->string[pos])) &&
+ row->expanded <= field->cols)
+ 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((unsigned char)row->string[pos])) &&
+ (pos != row->length - 1))
+ pos++;
+
+ if (split_line(field, FALSE, pos, &row) != E_OK) {
+ wrap_err = E_REQUEST_DENIED;
+ goto restore_and_exit;
+ }
+ } else
+ /* line is exactly the right length, do next one */
+ row = row->next;
+ }
+
+ /* Check if we have not run out of room */
+ if ((((field->opts & O_STATIC) == O_STATIC) &&
+ field->row_count > (field->rows + field->nrows)) ||
+ ((field->max != 0) && (field->row_count > field->max))) {
+
+ wrap_err = E_REQUEST_DENIED;
+
+ restore_and_exit:
+ if (saved_row->prev == NULL) {
+ field->alines = row_backup;
+ } else {
+ saved_row->prev->next = row_backup;
+ row_backup->prev = saved_row->prev;
+ }
+
+ field->row_xpos = saved_xpos;
+ field->cursor_xpos = saved_cur_xpos;
+ field->cursor_ypos = saved_ypos;
+ field->row_count = saved_row_count;
+ field->start_line = saved_start_line;
+ field->cur_line = saved_cur_line;
+
+ destroy_row_list(saved_row);
+ return wrap_err;
+ }
+
+ destroy_row_list(row_backup);
+ 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, _FORMI_FIELD_LINES **rowp, int direction)
+{
+ int old_len, count;
+ struct _formi_field_lines *saved;
+ char *newp;
+ _FORMI_FIELD_LINES *row = *rowp;
+#ifdef DEBUG
+ int dbg_ok = FALSE;
+
+ if (_formi_create_dbg_file() == E_OK) {
+ dbg_ok = TRUE;
+ }
+
+ if (dbg_ok == TRUE) {
+ fprintf(dbg, "join_line: working on row %p, row_count = %d\n",
+ row, field->row_count);
+ }
+#endif
+
+ if ((direction == JOIN_NEXT) || (direction == JOIN_NEXT_NW)) {
+ /*
+ * See if there is another line following, or if the
+ * line contains a hard return then we don't join
+ * any lines to it.
+ */
+ if ((row->next == NULL) || (row->hard_ret == TRUE)) {
+ return E_REQUEST_DENIED;
+ }
+
+#ifdef DEBUG
+ if (dbg_ok == TRUE) {
+ fprintf(dbg,
+ "join_line: join_next before length = %d, expanded = %d",
+ row->length, row->expanded);
+ fprintf(dbg,
+ " :: next row length = %d, expanded = %d\n",
+ row->length, row->expanded);
+ }
+#endif
+
+ if (row->allocated < (row->length + row->next->length + 1)) {
+ if ((newp = realloc(row->string, (size_t)(row->length +
+ row->next->length
+ + 1))) == NULL)
+ return E_REQUEST_DENIED;
+ row->string = newp;
+ row->allocated = row->length + row->next->length + 1;
+ }
+
+ strcat(row->string, row->next->string);
+ old_len = row->length;
+ row->length += row->next->length;
+ if (row->length > 0)
+ row->expanded =
+ _formi_tab_expanded_length(row->string, 0,
+ row->length - 1);
+ else
+ row->expanded = 0;
+
+ _formi_calculate_tabs(row);
+ row->hard_ret = row->next->hard_ret;
+
+ /* adjust current line if it is on the row being eaten */
+ if (field->cur_line == row->next) {
+ field->cur_line = row;
+ field->row_xpos += old_len;
+ field->cursor_xpos =
+ _formi_tab_expanded_length(row->string, 0,
+ field->row_xpos);
+ if (field->cursor_xpos > 0)
+ field->cursor_xpos--;
+
+ if (field->cursor_ypos > 0)
+ field->cursor_ypos--;
+ else {
+ if (field->start_line->prev != NULL)
+ field->start_line =
+ field->start_line->prev;
+ }
+ }
+
+ /* remove joined line record from the row list */
+ add_to_free(field, row->next);
+
+#ifdef DEBUG
+ if (dbg_ok == TRUE) {
+ fprintf(dbg,
+ "join_line: exit length = %d, expanded = %d\n",
+ row->length, row->expanded);
+ }
+#endif
+ } else {
+ if (row->prev == NULL) {
+ return E_REQUEST_DENIED;
+ }
+
+ saved = row->prev;
+
+ /*
+ * Don't try to join if the line above has a hard
+ * return on it.
+ */
+ if (saved->hard_ret == TRUE) {
+ return E_REQUEST_DENIED;
+ }
+
+#ifdef DEBUG
+ if (dbg_ok == TRUE) {
+ fprintf(dbg,
+ "join_line: join_prev before length = %d, expanded = %d",
+ row->length, row->expanded);
+ fprintf(dbg,
+ " :: prev row length = %d, expanded = %d\n",
+ saved->length, saved->expanded);
+ }
+#endif
+
+ if (saved->allocated < (row->length + saved->length + 1)) {
+ if ((newp = realloc(saved->string,
+ (size_t) (row->length +
+ saved->length
+ + 1))) == NULL)
+ return E_REQUEST_DENIED;
+ saved->string = newp;
+ saved->allocated = row->length + saved->length + 1;
+ }
+
+ strcat(saved->string, row->string);
+ old_len = saved->length;
+ saved->length += row->length;
+ if (saved->length > 0)
+ saved->expanded =
+ _formi_tab_expanded_length(saved->string, 0,
+ saved->length - 1);
+ else
+ saved->length = 0;
+
+ saved->hard_ret = row->hard_ret;
+
+ /* adjust current line if it was on the row being eaten */
+ if (field->cur_line == row) {
+ field->cur_line = saved;
+ field->row_xpos += old_len;
+ field->cursor_xpos =
+ _formi_tab_expanded_length(saved->string, 0,
+ field->row_xpos);
+ if (field->cursor_xpos > 0)
+ field->cursor_xpos--;
+ }
+
+ add_to_free(field, row);
+
+#ifdef DEBUG
+ if (dbg_ok == TRUE) {
+ fprintf(dbg,
+ "join_line: exit length = %d, expanded = %d\n",
+ saved->length, saved->expanded);
+ }
+#endif
+ row = saved;
+ }
+
+
+ /*
+ * Work out where the line lies in the field in relation to
+ * the cursor_ypos. First count the rows from the start of
+ * the field until we hit the row we just worked on.
+ */
+ saved = field->start_line;
+ count = 0;
+ while (saved->next != NULL) {
+ if (saved == row)
+ break;
+ count++;
+ saved = saved->next;
+ }
+
+ /* now check if we need to adjust cursor_ypos */
+ if (field->cursor_ypos > count) {
+ field->cursor_ypos--;
+ }
+
+ field->row_count--;
+ *rowp = row;
+
+ /* wrap the field if required, if this fails undo the change */
+ if ((direction == JOIN_NEXT) || (direction == JOIN_PREV)) {
+ if (_formi_wrap_field(field, row) != E_OK) {
+ return E_REQUEST_DENIED;
+ }
+ }
+
+ return E_OK;
+}
+
+/*
+ * Split the line at the given position, if possible. If hard_split is
+ * TRUE then split the line regardless of the position, otherwise don't
+ * split at the beginning of a line.
+ */
+static int
+split_line(FIELD *field, bool hard_split, unsigned pos,
+ _FORMI_FIELD_LINES **rowp)
+{
+ struct _formi_field_lines *new_line;
+ char *newp;
+ _FORMI_FIELD_LINES *row = *rowp;
+#ifdef DEBUG
+ short dbg_ok = FALSE;
+#endif
+
+ /* if asked to split right where the line already starts then
+ * just return - nothing to do unless we are appending a line
+ * to the buffer.
+ */
+ if ((pos == 0) && (hard_split == FALSE))
+ return E_OK;
+
+#ifdef DEBUG
+ if (_formi_create_dbg_file() == E_OK) {
+ fprintf(dbg, "split_line: splitting line at %d\n", pos);
+ dbg_ok = TRUE;
+ }
+#endif
+
+ /* Need an extra line struct, check free list first */
+ if (field->free != NULL) {
+ new_line = field->free;
+ field->free = new_line->next;
+ if (field->free != NULL)
+ field->free->prev = NULL;
+ } else {
+ if ((new_line = (struct _formi_field_lines *)
+ malloc(sizeof(struct _formi_field_lines))) == NULL)
+ return E_SYSTEM_ERROR;
+ new_line->prev = NULL;
+ new_line->next = NULL;
+ new_line->allocated = 0;
+ new_line->length = 0;
+ new_line->expanded = 0;
+ new_line->string = NULL;
+ new_line->hard_ret = FALSE;
+ new_line->tabs = NULL;
+ }
+
+#ifdef DEBUG
+ if (dbg_ok == TRUE) {
+ fprintf(dbg,
+ "split_line: enter: length = %d, expanded = %d\n",
+ row->length, row->expanded);
+ }
+#endif
+
+ assert((row->length < INT_MAX) && (row->expanded < INT_MAX));
+
+
+ /* add new line to the row list */
+ new_line->next = row->next;
+ new_line->prev = row;
+ row->next = new_line;
+ if (new_line->next != NULL)
+ new_line->next->prev = new_line;
+
+ new_line->length = row->length - pos;
+ if (new_line->length >= new_line->allocated) {
+ if ((newp = realloc(new_line->string,
+ (size_t) new_line->length + 1)) == NULL)
+ return E_SYSTEM_ERROR;
+ new_line->string = newp;
+ new_line->allocated = new_line->length + 1;
+ }
+
+ strcpy(new_line->string, &row->string[pos]);
+
+ row->length = pos;
+ row->string[pos] = '\0';
+
+ if (row->length != 0)
+ row->expanded = _formi_tab_expanded_length(row->string, 0,
+ row->length - 1);
+ else
+ row->expanded = 0;
+ _formi_calculate_tabs(row);
+
+ if (new_line->length != 0)
+ new_line->expanded =
+ _formi_tab_expanded_length(new_line->string, 0,
+ new_line->length - 1);
+ else
+ new_line->expanded = 0;
+
+ _formi_calculate_tabs(new_line);
+
+ /*
+ * If the given row was the current line then adjust the
+ * current line pointer if necessary
+ */
+ if ((field->cur_line == row) && (field->row_xpos >= pos)) {
+ field->cur_line = new_line;
+ field->row_xpos -= pos;
+ field->cursor_xpos =
+ _formi_tab_expanded_length(new_line->string, 0,
+ field->row_xpos);
+ if (field->cursor_xpos > 0)
+ field->cursor_xpos--;
+
+ field->cursor_ypos++;
+ if (field->cursor_ypos >= field->rows) {
+ if (field->start_line->next != NULL) {
+ field->start_line = field->start_line->next;
+ field->cursor_ypos = field->rows - 1;
+ }
+ else
+ assert(field->start_line->next == NULL);
+ }
+ }
+
+ /*
+ * If the line split had a hard return then replace the
+ * current line's hard return with a soft return and carry
+ * the hard return onto the line after.
+ */
+ if (row->hard_ret == TRUE) {
+ new_line->hard_ret = TRUE;
+ row->hard_ret = FALSE;
+ }
+
+ /*
+ * except where we are doing a hard split then the current
+ * row must have a hard return on it too...
+ */
+ if (hard_split == TRUE) {
+ row->hard_ret = TRUE;
+ }
+
+ assert(((row->expanded < INT_MAX) &&
+ (new_line->expanded < INT_MAX) &&
+ (row->length < INT_MAX) &&
+ (new_line->length < INT_MAX)));
+
+#ifdef DEBUG
+ if (dbg_ok == TRUE) {
+ fprintf(dbg, "split_line: exit: ");
+ fprintf(dbg, "row.length = %d, row.expanded = %d, ",
+ row->length, row->expanded);
+ fprintf(dbg,
+ "next_line.length = %d, next_line.expanded = %d, ",
+ new_line->length, new_line->expanded);
+ fprintf(dbg, "row_count = %d\n", field->row_count + 1);
+ }
+#endif
+
+ field->row_count++;
+ *rowp = new_line;
+
+ 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((unsigned char)string[i]))
+ i++;
+
+ return i;
+}
+
+/*
+ * Skip the blanks in the string associated with the given row, pass back
+ * the row and the offset at which the first non-blank is found. If no
+ * non-blank character is found then return the index to the last
+ * character on the last line.
+ */
+
+unsigned
+field_skip_blanks(unsigned int start, _FORMI_FIELD_LINES **rowp)
+{
+ unsigned int i;
+ _FORMI_FIELD_LINES *row, *last = NULL;
+
+ row = *rowp;
+ i = start;
+
+ do {
+ i = _formi_skip_blanks(&row->string[i], i);
+ if (!isblank((unsigned char)row->string[i])) {
+ last = row;
+ row = row->next;
+ /*
+ * don't reset if last line otherwise we will
+ * not be at the end of the string.
+ */
+ if (row != NULL)
+ i = 0;
+ } else
+ break;
+ }
+ while (row != NULL);
+
+ /*
+ * If we hit the end of the row list then point at the last row
+ * otherwise we return the row we found the blank on.
+ */
+ if (row == NULL)
+ *rowp = last;
+ else
+ *rowp = row;
+
+ 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. If do_join is TRUE then lines will be
+ * joined (without wrapping) until either the end of the field or the
+ * end of a word is found (whichever comes first).
+ */
+static int
+find_eow(FIELD *cur, unsigned int offset, bool do_join,
+ _FORMI_FIELD_LINES **rowp)
+{
+ int start;
+ _FORMI_FIELD_LINES *row;
+
+ row = *rowp;
+ start = offset;
+
+ do {
+ /* first skip any non-whitespace */
+ while ((row->string[start] != '\0')
+ && !isblank((unsigned char)row->string[start]))
+ start++;
+
+ /* see if we hit the end of the string */
+ if (row->string[start] == '\0') {
+ if (do_join == TRUE) {
+ if (row->next == NULL)
+ return start;
+
+ if (_formi_join_line(cur, &row, JOIN_NEXT_NW)
+ != E_OK)
+ return E_REQUEST_DENIED;
+ } else {
+ do {
+ if (row->next == NULL) {
+ *rowp = row;
+ return start;
+ } else {
+ row = row->next;
+ start = 0;
+ }
+ } while (row->length == 0);
+ }
+ }
+ } while (!isblank((unsigned char)row->string[start]));
+
+ do {
+ /* otherwise skip the whitespace.... */
+ while ((row->string[start] != '\0')
+ && isblank((unsigned char)row->string[start]))
+ start++;
+
+ if (row->string[start] == '\0') {
+ if (do_join == TRUE) {
+ if (row->next == NULL)
+ return start;
+
+ if (_formi_join_line(cur, &row, JOIN_NEXT_NW)
+ != E_OK)
+ return E_REQUEST_DENIED;
+ } else {
+ do {
+ if (row->next == NULL) {
+ *rowp = row;
+ return start;
+ } else {
+ row = row->next;
+ start = 0;
+ }
+ } while (row->length == 0);
+ }
+ }
+ } while (isblank((unsigned char)row->string[start]));
+
+ *rowp = row;
+ return start;
+}
+
+/*
+ * Find the beginning of the current word in the string str, starting
+ * at offset.
+ */
+static int
+find_sow(unsigned int offset, _FORMI_FIELD_LINES **rowp)
+{
+ int start;
+ char *str;
+ _FORMI_FIELD_LINES *row;
+
+ row = *rowp;
+ str = row->string;
+ start = offset;
+
+ do {
+ if (start > 0) {
+ if (isblank((unsigned char)str[start]) ||
+ isblank((unsigned char)str[start - 1])) {
+ if (isblank((unsigned char)str[start - 1]))
+ start--;
+ /* skip the whitespace.... */
+ while ((start >= 0) &&
+ isblank((unsigned char)str[start]))
+ start--;
+ }
+ }
+
+ /* see if we hit the start of the string */
+ if (start < 0) {
+ do {
+ if (row->prev == NULL) {
+ *rowp = row;
+ start = 0;
+ return start;
+ } else {
+ row = row->prev;
+ str = row->string;
+ if (row->length > 0)
+ start = row->length - 1;
+ else
+ start = 0;
+ }
+ } while (row->length == 0);
+ }
+ } while (isblank((unsigned char)row->string[start]));
+
+ /* see if we hit the start of the string */
+ if (start < 0) {
+ *rowp = row;
+ return 0;
+ }
+
+ /* now skip any non-whitespace */
+ do {
+ while ((start >= 0) && !isblank((unsigned char)str[start]))
+ start--;
+
+
+ if (start < 0) {
+ do {
+ if (row->prev == NULL) {
+ *rowp = row;
+ start = 0;
+ return start;
+ } else {
+ row = row->prev;
+ str = row->string;
+ if (row->length > 0)
+ start = row->length - 1;
+ else
+ start = 0;
+ }
+ } while (row->length == 0);
+ }
+ } while (!isblank((unsigned char)str[start]));
+
+ if (start > 0) {
+ start++; /* last loop has us pointing at a space, adjust */
+ if (start >= row->length) {
+ if (row->next != NULL) {
+ start = 0;
+ row = row->next;
+ } else {
+ start = row->length - 1;
+ }
+ }
+ }
+
+ if (start < 0)
+ start = 0;
+
+ *rowp = row;
+ return start;
+}
+
+/*
+ * Scroll the field forward the given number of lines.
+ */
+static void
+_formi_scroll_fwd(FIELD *field, unsigned int amt)
+{
+ unsigned int count;
+ _FORMI_FIELD_LINES *end_row;
+
+ end_row = field->start_line;
+ /* walk the line structs forward to find the bottom of the field */
+ count = field->rows - 1;
+ while ((count > 0) && (end_row->next != NULL))
+ {
+ count--;
+ end_row = end_row->next;
+ }
+
+ /* check if there are lines to scroll */
+ if ((count > 0) && (end_row->next == NULL))
+ return;
+
+ /*
+ * ok, lines to scroll - do this by walking both the start_line
+ * and the end_row at the same time for amt lines, we stop when
+ * either we have done the number of lines or end_row hits the
+ * last line in the field.
+ */
+ count = amt;
+ while ((count > 0) && (end_row->next != NULL)) {
+ count--;
+ field->start_line = field->start_line->next;
+ end_row = end_row->next;
+ }
+}
+
+/*
+ * Scroll the field backward the given number of lines.
+ */
+static void
+_formi_scroll_back(FIELD *field, unsigned int amt)
+{
+ unsigned int count;
+
+ /* check for lines above */
+ if (field->start_line->prev == NULL)
+ return;
+
+ /*
+ * Backward scroll is easy, follow row struct chain backward until
+ * the number of lines done or we reach the top of the field.
+ */
+ count = amt;
+ while ((count > 0) && (field->start_line->prev != NULL)) {
+ count--;
+ field->start_line = field->start_line->prev;
+ }
+}
+
+/*
+ * Scroll the field forward the given number of characters.
+ */
+void
+_formi_hscroll_fwd(FIELD *field, _FORMI_FIELD_LINES *row, int unsigned amt)
+{
+ unsigned int end, scroll_amt, expanded;
+ _formi_tab_t *ts;
+
+
+ if ((row->tabs == NULL) || (row->tabs->in_use == FALSE)) {
+ /* if the line has no tabs things are easy... */
+ end = field->start_char + field->cols + amt - 1;
+ scroll_amt = amt;
+ if (end > row->length) {
+ end = row->length;
+ scroll_amt = end - field->start_char - field->cols + 1;
+ }
+ } else {
+ /*
+ * If there are tabs we need to add on the scroll amount,
+ * find the last char position that will fit into
+ * the field and finally fix up the start_char. This
+ * is a lot of work but handling the case where there
+ * are not enough chars to scroll by amt is difficult.
+ */
+ end = field->start_char + field->row_xpos + amt;
+ if (end >= row->length)
+ end = row->length - 1;
+ else {
+ expanded = _formi_tab_expanded_length(
+ row->string,
+ field->start_char + amt,
+ field->start_char + field->row_xpos + amt);
+ ts = row->tabs;
+ /* skip tabs to the lhs of our starting point */
+ while ((ts != NULL) && (ts->in_use == TRUE)
+ && (ts->pos < end))
+ ts = ts->fwd;
+
+ while ((expanded <= field->cols)
+ && (end < row->length)) {
+ if (row->string[end] == '\t') {
+ assert((ts != NULL)
+ && (ts->in_use == TRUE));
+ if (ts->pos == end) {
+ if ((expanded + ts->size)
+ > field->cols)
+ break;
+ expanded += ts->size;
+ ts = ts->fwd;
+ }
+ else
+ assert(ts->pos == end);
+ } else
+ expanded++;
+ end++;
+ }
+ }
+
+ scroll_amt = tab_fit_window(field, end, field->cols);
+ if (scroll_amt < field->start_char)
+ scroll_amt = 1;
+ else
+ scroll_amt -= field->start_char;
+
+ scroll_amt = min(scroll_amt, amt);
+ }
+
+ field->start_char += scroll_amt;
+ field->cursor_xpos =
+ _formi_tab_expanded_length(row->string,
+ field->start_char,
+ field->row_xpos
+ + field->start_char) - 1;
+
+}
+
+/*
+ * Scroll the field backward the given number of characters.
+ */
+void
+_formi_hscroll_back(FIELD *field, _FORMI_FIELD_LINES *row, unsigned int amt)
+{
+ field->start_char -= min(field->start_char, amt);
+ field->cursor_xpos =
+ _formi_tab_expanded_length(row->string, field->start_char,
+ field->row_xpos
+ + field->start_char) - 1;
+ if (field->cursor_xpos >= field->cols) {
+ field->row_xpos = 0;
+ field->cursor_xpos = 0;
+ }
+}
+
+/*
+ * 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, j, start, line;
+ unsigned int tab, cpos, len;
+ char *str, c;
+ FIELD *cur;
+ _FORMI_FIELD_LINES *row;
+#ifdef DEBUG
+ char buffer[100];
+#endif
+
+ cur = form->fields[field];
+ flen = cur->cols;
+ slen = 0;
+ start = 0;
+ line = 0;
+
+ for (row = cur->start_line; ((row != NULL) && (line < cur->rows));
+ row = row->next, line++) {
+ wmove(form->scrwin, (int) (cur->form_row + line),
+ (int) cur->form_col);
+ if ((cur->rows + cur->nrows) == 1) {
+ if ((cur->cols + cur->start_char) >= row->length)
+ len = row->length;
+ else
+ len = cur->cols + cur->start_char;
+ if (row->string != NULL)
+ slen = _formi_tab_expanded_length(
+ row->string, cur->start_char, len);
+ else
+ slen = 0;
+
+ if (slen > cur->cols)
+ slen = cur->cols;
+ slen += cur->start_char;
+ } else
+ slen = row->expanded;
+
+ 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);
+
+ str = &row->string[cur->start_char];
+
+#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) {
+ if (row->expanded != 0) {
+ strncpy(buffer, str, flen);
+ } else {
+ strcpy(buffer, "(empty)");
+ }
+ } 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, cpos = cur->start_char; i < min(slen, flen);
+ i++, str++, cpos++)
+ {
+ c = *str;
+ tab = 0; /* just to shut gcc up */
+#ifdef DEBUG
+ fprintf(dbg, "adding char str[%d]=%c\n",
+ cpos + cur->start_char, c);
+#endif
+ if (((cur->opts & O_PUBLIC) != O_PUBLIC)) {
+ if (c == '\t')
+ tab = add_tab(form, row, cpos,
+ cur->pad);
+ else
+ waddch(form->scrwin, cur->pad);
+ } else if ((cur->opts & O_VISIBLE) == O_VISIBLE) {
+ if (c == '\t')
+ tab = add_tab(form, row, cpos, ' ');
+ else
+ waddch(form->scrwin, c);
+ } else {
+ if (c == '\t')
+ tab = add_tab(form, row, cpos, ' ');
+ else
+ waddch(form->scrwin, ' ');
+ }
+
+ /*
+ * If we have had a tab then skip forward
+ * the requisite number of chars to keep
+ * things in sync.
+ */
+ if (c == '\t')
+ i += tab - 1;
+ }
+
+ for (i = 0; i < post; i++)
+ waddch(form->scrwin, cur->pad);
+ }
+
+ for (i = line; i < cur->rows; i++) {
+ wmove(form->scrwin, (int) (cur->form_row + i),
+ (int) cur->form_col);
+
+ if (form->cur_field == field)
+ wattrset(form->scrwin, cur->fore);
+ else
+ wattrset(form->scrwin, cur->back);
+
+ for (j = 0; j < cur->cols; j++) {
+ waddch(form->scrwin, cur->pad);
+ }
+ }
+
+ wattrset(form->scrwin, cur->back);
+ return;
+}
+
+/*
+ * Add the correct number of the given character to simulate a tab
+ * in the field.
+ */
+static int
+add_tab(FORM *form, _FORMI_FIELD_LINES *row, unsigned int i, char c)
+{
+ int j;
+ _formi_tab_t *ts = row->tabs;
+
+ while ((ts != NULL) && (ts->pos != i))
+ ts = ts->fwd;
+
+ assert(ts != NULL);
+
+ for (j = 0; j < ts->size; j++)
+ waddch(form->scrwin, c);
+
+ return ts->size;
+}
+
+
+/*
+ * 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;
+ _FORMI_FIELD_LINES *row, *temp, *next_temp;
+
+ row = field->cur_line;
+
+ /*
+ * If buffer has not had a string before, set it to a blank
+ * string. Everything should flow from there....
+ */
+ if (row->string == NULL) {
+ if ((row->string = (char *) malloc((size_t)INITIAL_LINE_ALLOC))
+ == NULL)
+ return E_SYSTEM_ERROR;
+ row->string[0] = '\0';
+ row->allocated = INITIAL_LINE_ALLOC;
+ row->length = 0;
+ row->expanded = 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;
+ }
+
+ if ((c == '\t') && (field->cols <= 8)) {
+#ifdef DEBUG
+ fprintf(dbg, "add_char: field too small for a tab\n");
+#endif
+ return E_NO_ROOM;
+ }
+
+#ifdef DEBUG
+ fprintf(dbg, "add_char: pos=%d, char=%c\n", pos, c);
+ fprintf(dbg, "add_char enter: xpos=%d, row_pos=%d, start=%d\n",
+ field->cursor_xpos, field->row_xpos, field->start_char);
+ fprintf(dbg, "add_char enter: length=%d(%d), allocated=%d\n",
+ row->expanded, row->length, row->allocated);
+ fprintf(dbg, "add_char enter: %s\n", row->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->row_xpos + field->start_char) == 0)) {
+ row = field->alines;
+ if (row->next != NULL) {
+ /* shift all but one line structs to free list */
+ temp = row->next;
+ do {
+ next_temp = temp->next;
+ add_to_free(field, temp);
+ temp = next_temp;
+ } while (temp != NULL);
+ }
+
+ row->length = 0;
+ row->string[0] = '\0';
+ pos = 0;
+ field->start_char = 0;
+ field->start_line = row;
+ field->cur_line = row;
+ field->row_count = 1;
+ field->row_xpos = 0;
+ field->cursor_ypos = 0;
+ row->expanded = 0;
+ row->length = 0;
+ _formi_init_field_xpos(field);
+ }
+
+
+ if ((field->overlay == 0)
+ || ((field->overlay == 1) && (pos >= row->length))) {
+ /* first check if the field can have more chars...*/
+ if (check_field_size(field) == FALSE)
+ return E_REQUEST_DENIED;
+
+ if (row->length + 2
+ >= row->allocated) {
+ new_size = row->allocated + 16 - (row->allocated % 16);
+ if ((new = (char *) realloc(row->string,
+ (size_t) new_size )) == NULL)
+ return E_SYSTEM_ERROR;
+ row->allocated = new_size;
+ row->string = new;
+ }
+ }
+
+ if ((field->overlay == 0) && (row->length > pos)) {
+ bcopy(&row->string[pos], &row->string[pos + 1],
+ (size_t) (row->length - pos + 1));
+ }
+
+ old_c = row->string[pos];
+ row->string[pos] = c;
+ if (pos >= row->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.
+ */
+ row->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 >= row->length))) {
+ row->length++;
+ }
+
+ _formi_calculate_tabs(row);
+ row->expanded = _formi_tab_expanded_length(row->string, 0,
+ row->length - 1);
+
+ /* wrap the field, if needed */
+ status = _formi_wrap_field(field, row);
+
+ row = field->cur_line;
+ pos = field->row_xpos;
+
+ /*
+ * 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 >= (row->length - 1) /*XXXX- append check???*/))) {
+ /*
+ * wrap failed for some reason, back out the
+ * char insert
+ */
+ bcopy(&row->string[pos + 1], &row->string[pos],
+ (size_t) (row->length - pos));
+ row->length--;
+ if (pos > 0)
+ pos--;
+ } else if (field->overlay == 1) {
+ /* back out character overlay */
+ row->string[pos] = old_c;
+ }
+
+ _formi_calculate_tabs(row);
+
+ _formi_wrap_field(field, row);
+ /*
+ * If we are here then either the status is bad or we
+ * simply ran out of room. If the status is E_OK then
+ * we ran out of room, let the form driver know this.
+ */
+ if (status == E_OK)
+ status = E_REQUEST_DENIED;
+
+ } else {
+ field->buf0_status = TRUE;
+ field->row_xpos++;
+ if ((field->rows + field->nrows) == 1) {
+ status = _formi_set_cursor_xpos(field, FALSE);
+ } else {
+ field->cursor_xpos =
+ _formi_tab_expanded_length(
+ row->string, 0, field->row_xpos - 1);
+
+ /*
+ * 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 = field->start_line->next;
+ }
+ }
+ }
+
+ assert((field->cursor_xpos <= field->cols)
+ && (field->cursor_ypos < 400000));
+
+#ifdef DEBUG
+ fprintf(dbg, "add_char exit: xpos=%d, row_pos=%d, start=%d\n",
+ field->cursor_xpos, field->row_xpos, field->start_char);
+ fprintf(dbg, "add_char_exit: length=%d(%d), allocated=%d\n",
+ row->expanded, row->length, row->allocated);
+ fprintf(dbg, "add_char exit: ypos=%d, start_line=%p\n",
+ field->cursor_ypos, field->start_line);
+ fprintf(dbg,"add_char exit: %s\n", row->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;
+}
+
+/*
+ * Set the position of the cursor on the screen in the row depending on
+ * where the current position in the string is and the justification
+ * that is to be applied to the field. Justification is only applied
+ * to single row, static fields.
+ */
+static int
+_formi_set_cursor_xpos(FIELD *field, int noscroll)
+{
+ int just, pos;
+
+ just = field->justification;
+ pos = field->start_char + field->row_xpos;
+
+#ifdef DEBUG
+ fprintf(dbg,
+ "cursor_xpos enter: pos %d, start_char %d, row_xpos %d, xpos %d\n",
+ pos, field->start_char, field->row_xpos, field->cursor_xpos);
+#endif
+
+ /*
+ * make sure we apply the correct justification to non-static
+ * fields.
+ */
+ if (((field->rows + field->nrows) != 1) ||
+ ((field->opts & O_STATIC) != O_STATIC))
+ just = JUSTIFY_LEFT;
+
+ switch (just) {
+ case JUSTIFY_RIGHT:
+ field->cursor_xpos = field->cols - 1
+ - _formi_tab_expanded_length(
+ field->cur_line->string, 0,
+ field->cur_line->length - 1)
+ + _formi_tab_expanded_length(
+ field->cur_line->string, 0,
+ field->row_xpos);
+ break;
+
+ case JUSTIFY_CENTER:
+ field->cursor_xpos = ((field->cols - 1)
+ - _formi_tab_expanded_length(
+ field->cur_line->string, 0,
+ field->cur_line->length - 1) + 1) / 2
+ + _formi_tab_expanded_length(field->cur_line->string,
+ 0, field->row_xpos);
+
+ if (field->cursor_xpos > (field->cols - 1))
+ field->cursor_xpos = (field->cols - 1);
+ break;
+
+ default:
+ field->cursor_xpos = _formi_tab_expanded_length(
+ field->cur_line->string,
+ field->start_char,
+ field->row_xpos + field->start_char);
+ if ((field->cursor_xpos <= (field->cols - 1)) &&
+ ((field->start_char + field->row_xpos)
+ < field->cur_line->length))
+ field->cursor_xpos--;
+
+ if (field->cursor_xpos > (field->cols - 1)) {
+ if ((field->opts & O_STATIC) == O_STATIC) {
+ field->start_char = 0;
+
+ if (field->row_xpos
+ == (field->cur_line->length - 1)) {
+ field->cursor_xpos = field->cols - 1;
+ } else {
+ field->cursor_xpos =
+ _formi_tab_expanded_length(
+ field->cur_line->string,
+ field->start_char,
+ field->row_xpos
+ + field->start_char
+ - 1) - 1;
+ }
+ } else {
+ if (noscroll == FALSE) {
+ field->start_char =
+ tab_fit_window(
+ field,
+ field->start_char
+ + field->row_xpos,
+ field->cols);
+ field->row_xpos = pos
+ - field->start_char;
+ field->cursor_xpos =
+ _formi_tab_expanded_length(
+ field->cur_line->string,
+ field->start_char,
+ field->row_xpos
+ + field->start_char - 1);
+ } else {
+ field->cursor_xpos = (field->cols - 1);
+ }
+ }
+
+ }
+ break;
+ }
+
+#ifdef DEBUG
+ fprintf(dbg,
+ "cursor_xpos exit: pos %d, start_char %d, row_xpos %d, xpos %d\n",
+ pos, field->start_char, field->row_xpos, field->cursor_xpos);
+#endif
+ return E_OK;
+}
+
+/*
+ * 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 start, end, pos, status, old_count, size;
+ unsigned int old_xpos, old_row_pos;
+ int len, wb;
+ bool eat_char;
+ _FORMI_FIELD_LINES *row, *rs;
+
+ cur = form->fields[form->cur_field];
+ if (cur->cur_line->string == NULL)
+ return E_REQUEST_DENIED;
+
+#ifdef DEBUG
+ fprintf(dbg, "entry: request is REQ_%s\n", reqs[c - REQ_MIN_REQUEST]);
+ fprintf(dbg,
+ "entry: xpos=%d, row_pos=%d, start_char=%d, length=%d, allocated=%d\n",
+ cur->cursor_xpos, cur->row_xpos, cur->start_char,
+ cur->cur_line->length, cur->cur_line->allocated);
+ fprintf(dbg, "entry: start_line=%p, ypos=%d\n", cur->start_line,
+ cur->cursor_ypos);
+ fprintf(dbg, "entry: string=");
+ if (cur->cur_line->string == NULL)
+ fprintf(dbg, "(null)\n");
+ else
+ fprintf(dbg, "\"%s\"\n", cur->cur_line->string);
+#endif
+
+ /* Cannot manipulate a null string! */
+ if (cur->cur_line->string == NULL)
+ return E_REQUEST_DENIED;
+
+ saved = '\0';
+ row = cur->cur_line;
+
+ switch (c) {
+ case REQ_RIGHT_CHAR:
+ /*
+ * The right_char request performs the same function
+ * as the next_char request except that the cursor is
+ * not wrapped if it is at the end of the line, so
+ * check if the cursor is at the end of the line and
+ * deny the request otherwise just fall through to
+ * the next_char request handler.
+ */
+ if (cur->cursor_xpos >= cur->cols - 1)
+ return E_REQUEST_DENIED;
+
+ /* FALLTHRU */
+
+ 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->row_xpos == cur->cols - 1) &&
+ ((cur->rows + cur->nrows) == 1))
+ return E_REQUEST_DENIED;
+
+ if (((cur->rows + cur->nrows) == 1) &&
+ (cur->row_xpos + cur->start_char + 1) > row->length)
+ return E_REQUEST_DENIED;
+
+ if ((cur->rows + cur->nrows) == 1) {
+ cur->row_xpos++;
+ _formi_set_cursor_xpos(cur, (c == REQ_RIGHT_CHAR));
+ } else {
+ if (cur->cursor_xpos >= (row->expanded - 1)) {
+ if ((row->next == NULL) ||
+ (c == REQ_RIGHT_CHAR))
+ return E_REQUEST_DENIED;
+
+ cur->cursor_xpos = 0;
+ cur->row_xpos = 0;
+ cur->cur_line = cur->cur_line->next;
+ if (cur->cursor_ypos == (cur->rows - 1))
+ cur->start_line =
+ cur->start_line->next;
+ else
+ cur->cursor_ypos++;
+ } else {
+ old_xpos = cur->cursor_xpos;
+ old_row_pos = cur->row_xpos;
+ if (row->string[cur->row_xpos] == '\t')
+ cur->cursor_xpos += tab_size(row,
+ cur->row_xpos);
+ else
+ cur->cursor_xpos++;
+ cur->row_xpos++;
+ if (cur->cursor_xpos
+ >= row->expanded) {
+ if ((row->next == NULL) ||
+ (c == REQ_RIGHT_CHAR)) {
+ cur->cursor_xpos = old_xpos;
+ cur->row_xpos = old_row_pos;
+ return E_REQUEST_DENIED;
+ }
+
+ cur->cursor_xpos = 0;
+ cur->row_xpos = 0;
+ cur->cur_line = cur->cur_line->next;
+ if (cur->cursor_ypos
+ == (cur->rows - 1))
+ cur->start_line =
+ cur->start_line->next;
+ else
+ cur->cursor_ypos++;
+ }
+ }
+ }
+
+ break;
+
+ case REQ_LEFT_CHAR:
+ /*
+ * The behaviour of left_char is the same as prev_char
+ * except that the cursor will not wrap if it has
+ * reached the LHS of the field, so just check this
+ * and fall through if we are not at the LHS.
+ */
+ if (cur->cursor_xpos == 0)
+ return E_REQUEST_DENIED;
+
+ /* FALLTHRU */
+ case REQ_PREV_CHAR:
+ if ((cur->rows + cur->nrows) == 1) {
+ if (cur->row_xpos == 0) {
+ if (cur->start_char > 0)
+ cur->start_char--;
+ else
+ return E_REQUEST_DENIED;
+ } else {
+ cur->row_xpos--;
+ _formi_set_cursor_xpos(cur, FALSE);
+ }
+ } else {
+ if ((cur->cursor_xpos == 0) &&
+ (cur->cursor_ypos == 0) &&
+ (cur->start_line->prev == NULL))
+ return E_REQUEST_DENIED;
+
+ pos = cur->row_xpos;
+ if (cur->cursor_xpos > 0) {
+ if (row->string[pos] == '\t') {
+ size = tab_size(row, pos);
+ if (size > cur->cursor_xpos) {
+ cur->cursor_xpos = 0;
+ cur->row_xpos = 0;
+ } else {
+ cur->row_xpos--;
+ cur->cursor_xpos -= size;
+ }
+ } else {
+ cur->cursor_xpos--;
+ cur->row_xpos--;
+ }
+ } else {
+ cur->cur_line = cur->cur_line->prev;
+ if (cur->cursor_ypos > 0)
+ cur->cursor_ypos--;
+ else
+ cur->start_line =
+ cur->start_line->prev;
+ row = cur->cur_line;
+ if (row->expanded > 0) {
+ cur->cursor_xpos = row->expanded - 1;
+ } else {
+ cur->cursor_xpos = 0;
+ }
+
+ if (row->length > 0)
+ cur->row_xpos = row->length - 1;
+ else
+ cur->row_xpos = 0;
+ }
+ }
+
+ break;
+
+ case REQ_DOWN_CHAR:
+ /*
+ * The down_char request has the same functionality as
+ * the next_line request excepting that the field is not
+ * scrolled if the cursor is at the bottom of the field.
+ * Check to see if the cursor is at the bottom of the field
+ * and if it is then deny the request otherwise fall
+ * through to the next_line handler.
+ */
+ if (cur->cursor_ypos >= cur->rows - 1)
+ return E_REQUEST_DENIED;
+
+ /* FALLTHRU */
+
+ case REQ_NEXT_LINE:
+ if ((row->next == NULL) || (cur->cur_line->next == NULL))
+ return E_REQUEST_DENIED;
+
+ cur->cur_line = cur->cur_line->next;
+ if ((cur->cursor_ypos + 1) >= cur->rows) {
+ cur->start_line = cur->start_line->next;
+ } else
+ cur->cursor_ypos++;
+ row = cur->cur_line;
+
+ if (row->length == 0) {
+ cur->row_xpos = 0;
+ cur->cursor_xpos = 0;
+ } else {
+ if (cur->cursor_xpos > (row->expanded - 1))
+ cur->cursor_xpos = row->expanded - 1;
+
+ cur->row_xpos = tab_fit_len(row, cur->cursor_xpos + 1);
+ if (cur->row_xpos == 0)
+ cur->cursor_xpos = 0;
+ else
+ cur->cursor_xpos =
+ _formi_tab_expanded_length(
+ row->string, 0, cur->row_xpos);
+ if (cur->cursor_xpos > 0)
+ cur->cursor_xpos--;
+ }
+ break;
+
+ case REQ_UP_CHAR:
+ /*
+ * The up_char request has the same functionality as
+ * the prev_line request excepting the field is not
+ * scrolled, check if the cursor is at the top of the
+ * field, if it is deny the request otherwise fall
+ * through to the prev_line handler.
+ */
+ if (cur->cursor_ypos == 0)
+ return E_REQUEST_DENIED;
+
+ /* FALLTHRU */
+
+ case REQ_PREV_LINE:
+ if (cur->cur_line->prev == NULL)
+ return E_REQUEST_DENIED;
+
+ if (cur->cursor_ypos == 0) {
+ if (cur->start_line->prev == NULL)
+ return E_REQUEST_DENIED;
+ cur->start_line = cur->start_line->prev;
+ } else
+ cur->cursor_ypos--;
+
+ cur->cur_line = cur->cur_line->prev;
+ row = cur->cur_line;
+
+ if (row->length == 0) {
+ cur->row_xpos = 0;
+ cur->cursor_xpos = 0;
+ } else {
+ if (cur->cursor_xpos > (row->expanded - 1))
+ cur->cursor_xpos = row->expanded - 1;
+
+ cur->row_xpos = tab_fit_len(row, cur->cursor_xpos + 1);
+ cur->cursor_xpos =
+ _formi_tab_expanded_length(row->string,
+ 0, cur->row_xpos);
+ if (cur->cursor_xpos > 0)
+ cur->cursor_xpos--;
+ }
+ break;
+
+ case REQ_NEXT_WORD:
+ start = cur->row_xpos + cur->start_char;
+ str = row->string;
+
+ wb = find_eow(cur, start, FALSE, &row);
+ if (wb < 0)
+ return wb;
+
+ start = wb;
+ /* 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 */
+ size = _formi_tab_expanded_length(str,
+ cur->start_char, start);
+ if (size < cur->cols) {
+ cur->row_xpos = start - cur->start_char;
+ } else {
+ cur->start_char = start;
+ cur->row_xpos = 0;
+ }
+ _formi_set_cursor_xpos(cur, FALSE);
+ } else {
+ /* multiline field */
+ cur->cur_line = row;
+ adjust_ypos(cur, row);
+
+ cur->row_xpos = start;
+ cur->cursor_xpos =
+ _formi_tab_expanded_length(
+ row->string, 0, cur->row_xpos) - 1;
+ }
+ break;
+
+ case REQ_PREV_WORD:
+ start = cur->start_char + cur->row_xpos;
+ if (cur->start_char > 0)
+ start--;
+
+ if ((start == 0) && (row->prev == NULL))
+ return E_REQUEST_DENIED;
+
+ if (start == 0) {
+ row = row->prev;
+ if (row->length > 0)
+ start = row->length - 1;
+ else
+ start = 0;
+ }
+
+ str = row->string;
+
+ start = find_sow(start, &row);
+
+ if ((cur->rows + cur->nrows) == 1) {
+ /* single line field */
+ size = _formi_tab_expanded_length(str,
+ cur->start_char, start);
+
+ if (start > cur->start_char) {
+ cur->row_xpos = start - cur->start_char;
+ } else {
+ cur->start_char = start;
+ cur->row_xpos = 0;
+ }
+ _formi_set_cursor_xpos(cur, FALSE);
+ } else {
+ /* multiline field */
+ cur->cur_line = row;
+ adjust_ypos(cur, row);
+ cur->row_xpos = start;
+ cur->cursor_xpos =
+ _formi_tab_expanded_length(
+ row->string, 0,
+ cur->row_xpos) - 1;
+ }
+
+ break;
+
+ case REQ_BEG_FIELD:
+ cur->start_char = 0;
+ while (cur->start_line->prev != NULL)
+ cur->start_line = cur->start_line->prev;
+ cur->cur_line = cur->start_line;
+ cur->row_xpos = 0;
+ _formi_init_field_xpos(cur);
+ cur->cursor_ypos = 0;
+ break;
+
+ case REQ_BEG_LINE:
+ cur->row_xpos = 0;
+ _formi_init_field_xpos(cur);
+ cur->start_char = 0;
+ break;
+
+ case REQ_END_FIELD:
+ while (cur->cur_line->next != NULL)
+ cur->cur_line = cur->cur_line->next;
+
+ if (cur->row_count > cur->rows) {
+ cur->start_line = cur->cur_line;
+ pos = cur->rows - 1;
+ while (pos > 0) {
+ cur->start_line = cur->start_line->prev;
+ pos--;
+ }
+ cur->cursor_ypos = cur->rows - 1;
+ } else {
+ 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:
+ row = cur->cur_line;
+
+ if ((cur->rows + cur->nrows) == 1) {
+ if (row->expanded > cur->cols - 1) {
+ if ((cur->opts & O_STATIC) != O_STATIC) {
+ cur->start_char = tab_fit_window(
+ cur, row->length,
+ cur->cols) + 1;
+ cur->row_xpos = row->length
+ - cur->start_char;
+ } else {
+ cur->start_char = 0;
+ cur->row_xpos = cur->cols - 1;
+ }
+ } else {
+ cur->row_xpos = row->length + 1;
+ cur->start_char = 0;
+ }
+ _formi_set_cursor_xpos(cur, FALSE);
+ } else {
+ cur->row_xpos = row->length - 1;
+ cur->cursor_xpos = row->expanded - 1;
+ if (row->next == NULL) {
+ cur->row_xpos++;
+ cur->cursor_xpos++;
+ }
+ }
+ break;
+
+ case REQ_NEW_LINE:
+ start = cur->start_char + cur->row_xpos;
+ if ((status = split_line(cur, TRUE, start, &row)) != E_OK)
+ return status;
+ cur->cur_line->hard_ret = TRUE;
+ cur->cursor_xpos = 0;
+ cur->row_xpos = 0;
+ break;
+
+ case REQ_INS_CHAR:
+ if ((status = _formi_add_char(cur, cur->start_char
+ + cur->row_xpos,
+ cur->pad)) != E_OK)
+ return status;
+ break;
+
+ case REQ_INS_LINE:
+ if ((status = split_line(cur, TRUE, 0, &row)) != E_OK)
+ return status;
+ cur->cur_line->hard_ret = TRUE;
+ break;
+
+ case REQ_DEL_CHAR:
+ row = cur->cur_line;
+ start = cur->start_char + cur->row_xpos;
+ end = row->length - 1;
+ if ((start >= row->length) && (row->next == NULL))
+ return E_REQUEST_DENIED;
+
+ if ((start == row->length - 1) || (row->length == 0)) {
+ if ((cur->rows + cur->nrows) > 1) {
+ /*
+ * Firstly, check if the current line has
+ * a hard return. In this case we just
+ * want to "delete" the hard return and
+ * re-wrap the field. The hard return
+ * does not occupy a character space in
+ * the buffer but we must make it appear
+ * like it does for a deletion.
+ */
+ if (row->hard_ret == TRUE) {
+ row->hard_ret = FALSE;
+ if (_formi_join_line(cur, &row,
+ JOIN_NEXT)
+ != E_OK) {
+ row->hard_ret = TRUE;
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ /*
+ * If we have more than one row, join the
+ * next row to make things easier unless
+ * we are at the end of the string, in
+ * that case the join would fail but we
+ * really want to delete the last char
+ * in the field.
+ */
+ if (row->next != NULL) {
+ if (_formi_join_line(cur, &row,
+ JOIN_NEXT_NW)
+ != E_OK) {
+ return E_REQUEST_DENIED;
+ }
+ }
+ }
+ }
+
+ saved = row->string[start];
+ bcopy(&row->string[start + 1], &row->string[start],
+ (size_t) (end - start + 1));
+ row->string[end] = '\0';
+ row->length--;
+ if (row->length > 0)
+ row->expanded = _formi_tab_expanded_length(
+ row->string, 0, row->length - 1);
+ else
+ row->expanded = 0;
+
+ /*
+ * recalculate tabs for a single line field, multiline
+ * fields will do this when the field is wrapped.
+ */
+ if ((cur->rows + cur->nrows) == 1)
+ _formi_calculate_tabs(row);
+ /*
+ * if we are at the end of the string then back the
+ * cursor pos up one to stick on the end of the line
+ */
+ if (start == row->length) {
+ if (row->length > 1) {
+ if ((cur->rows + cur->nrows) == 1) {
+ pos = cur->row_xpos + cur->start_char;
+ cur->start_char =
+ tab_fit_window(
+ cur,
+ cur->start_char + cur->row_xpos,
+ cur->cols);
+ cur->row_xpos = pos - cur->start_char
+ - 1;
+ _formi_set_cursor_xpos(cur, FALSE);
+ } else {
+ if (cur->row_xpos == 0) {
+ if (row->next != NULL) {
+ if (_formi_join_line(
+ cur, &row,
+ JOIN_PREV_NW)
+ != E_OK) {
+ return E_REQUEST_DENIED;
+ }
+ } else {
+ if (cur->row_count > 1)
+ cur->row_count--;
+ }
+
+ }
+
+ cur->row_xpos = start - 1;
+ cur->cursor_xpos =
+ _formi_tab_expanded_length(
+ row->string,
+ 0, cur->row_xpos - 1);
+ if ((cur->cursor_xpos > 0)
+ && (start != (row->expanded - 1)))
+ cur->cursor_xpos--;
+ }
+
+ start--;
+ } else {
+ start = 0;
+ cur->row_xpos = 0;
+ _formi_init_field_xpos(cur);
+ }
+ }
+
+ if ((cur->rows + cur->nrows) > 1) {
+ if (_formi_wrap_field(cur, row) != E_OK) {
+ bcopy(&row->string[start],
+ &row->string[start + 1],
+ (size_t) (end - start));
+ row->length++;
+ row->string[start] = saved;
+ _formi_wrap_field(cur, row);
+ return E_REQUEST_DENIED;
+ }
+ }
+ break;
+
+ case REQ_DEL_PREV:
+ if ((cur->cursor_xpos == 0) && (cur->start_char == 0)
+ && (cur->start_line->prev == NULL)
+ && (cur->cursor_ypos == 0))
+ return E_REQUEST_DENIED;
+
+ row = cur->cur_line;
+ start = cur->row_xpos + cur->start_char;
+ end = row->length - 1;
+ eat_char = TRUE;
+
+ if ((cur->start_char + cur->row_xpos) == 0) {
+ if (row->prev == NULL)
+ return E_REQUEST_DENIED;
+
+ /*
+ * If we are a multiline field then check if
+ * the line above has a hard return. If it does
+ * then just "eat" the hard return and re-wrap
+ * the field.
+ */
+ if (row->prev->hard_ret == TRUE) {
+ row->prev->hard_ret = FALSE;
+ if (_formi_join_line(cur, &row,
+ JOIN_PREV) != E_OK) {
+ row->prev->hard_ret = TRUE;
+ return 0;
+ }
+
+ eat_char = FALSE;
+ } else {
+ start = row->prev->length;
+ /*
+ * Join this line to the previous
+ * one.
+ */
+ if (_formi_join_line(cur, &row,
+ JOIN_PREV_NW) != E_OK) {
+ return 0;
+ }
+ end = row->length - 1;
+ }
+ }
+
+ if (eat_char == TRUE) {
+ /*
+ * eat a char from the buffer. Normally we do
+ * this unless we have deleted a "hard return"
+ * in which case we just want to join the lines
+ * without losing a char.
+ */
+ saved = row->string[start - 1];
+ bcopy(&row->string[start], &row->string[start - 1],
+ (size_t) (end - start + 1));
+ row->length--;
+ row->string[row->length] = '\0';
+ row->expanded = _formi_tab_expanded_length(
+ row->string, 0, row->length - 1);
+ }
+
+ if ((cur->rows + cur->nrows) == 1) {
+ _formi_calculate_tabs(row);
+ pos = cur->row_xpos + cur->start_char;
+ if (pos > 0)
+ pos--;
+ cur->start_char =
+ tab_fit_window(cur,
+ cur->start_char + cur->row_xpos,
+ cur->cols);
+ cur->row_xpos = pos - cur->start_char;
+ _formi_set_cursor_xpos(cur, FALSE);
+ } else {
+ if (eat_char == TRUE) {
+ cur->row_xpos--;
+ if (cur->row_xpos > 0)
+ cur->cursor_xpos =
+ _formi_tab_expanded_length(
+ row->string, 0,
+ cur->row_xpos - 1);
+ else
+ cur->cursor_xpos = 0;
+ }
+
+ if ((_formi_wrap_field(cur, row) != E_OK)) {
+ bcopy(&row->string[start - 1],
+ &row->string[start],
+ (size_t) (end - start));
+ row->length++;
+ row->string[start - 1] = saved;
+ row->string[row->length] = '\0';
+ _formi_wrap_field(cur, row);
+ return E_REQUEST_DENIED;
+ }
+ }
+ break;
+
+ case REQ_DEL_LINE:
+ if (((cur->rows + cur->nrows) == 1) ||
+ (cur->row_count == 1)) {
+ /* single line case */
+ row->length = 0;
+ row->expanded = row->length = 0;
+ cur->row_xpos = 0;
+ _formi_init_field_xpos(cur);
+ 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 (old_count == 1) {
+ row->expanded = row->length = 0;
+ cur->cursor_xpos = 0;
+ cur->row_xpos = 0;
+ cur->cursor_ypos = 0;
+ } else
+ add_to_free(cur, row);
+
+ if (row->next == NULL) {
+ if (cur->cursor_ypos == 0) {
+ if (cur->start_line->prev != NULL) {
+ cur->start_line =
+ cur->start_line->prev;
+ }
+ } else {
+ cur->cursor_ypos--;
+ }
+ }
+
+ if (old_count > 1) {
+ if (cur->cursor_xpos > row->expanded) {
+ cur->cursor_xpos = row->expanded - 1;
+ cur->row_xpos = row->length - 1;
+ }
+
+ cur->start_line = cur->alines;
+ rs = cur->start_line;
+ cur->cursor_ypos = 0;
+ while (rs != row) {
+ if (cur->cursor_ypos < cur->rows)
+ cur->cursor_ypos++;
+ else
+ cur->start_line =
+ cur->start_line->next;
+ rs = rs->next;
+ }
+ }
+ }
+ break;
+
+ case REQ_DEL_WORD:
+ start = cur->start_char + cur->row_xpos;
+ str = row->string;
+
+ wb = find_eow(cur, start, TRUE, &row);
+ if (wb < 0)
+ return wb;
+
+ end = wb;
+
+ /*
+ * If not at the start of a word then find the start,
+ * we cannot blindly call find_sow because this will
+ * skip back a word if we are already at the start of
+ * a word.
+ */
+ if ((start > 0)
+ && !(isblank((unsigned char)str[start - 1]) &&
+ !isblank((unsigned char)str[start])))
+ start = find_sow(start, &row);
+ str = row->string;
+ /* XXXX hmmmm what if start and end on diff rows? XXXX */
+ bcopy(&str[end], &str[start],
+ (size_t) (row->length - end + 1));
+ len = end - start;
+ row->length -= len;
+
+ if ((cur->rows + cur->nrows) > 1) {
+ row = cur->start_line + cur->cursor_ypos;
+ if (row->next != NULL) {
+ /*
+ * if not on the last row we need to
+ * join on the next row so the line
+ * will be re-wrapped.
+ */
+ _formi_join_line(cur, &row, JOIN_NEXT_NW);
+ }
+ _formi_wrap_field(cur, row);
+ cur->row_xpos = start;
+ cur->cursor_xpos = _formi_tab_expanded_length(
+ row->string, 0, cur->row_xpos);
+ if (cur->cursor_xpos > 0)
+ cur->cursor_xpos--;
+ } else {
+ _formi_calculate_tabs(row);
+ cur->row_xpos = start - cur->start_char;
+ if (cur->row_xpos > 0)
+ cur->row_xpos--;
+ _formi_set_cursor_xpos(cur, FALSE);
+ }
+ break;
+
+ case REQ_CLR_EOL:
+ row->string[cur->row_xpos + 1] = '\0';
+ row->length = cur->row_xpos + 1;
+ row->expanded = cur->cursor_xpos + 1;
+ break;
+
+ case REQ_CLR_EOF:
+ row = cur->cur_line->next;
+ while (row != NULL) {
+ rs = row->next;
+ add_to_free(cur, row);
+ row = rs;
+ cur->row_count--;
+ }
+ break;
+
+ case REQ_CLR_FIELD:
+ row = cur->alines->next;
+ cur->cur_line = cur->alines;
+ cur->start_line = cur->alines;
+
+ while (row != NULL) {
+ rs = row->next;
+ add_to_free(cur, row);
+ row = rs;
+ }
+
+ cur->alines->string[0] = '\0';
+ cur->alines->length = 0;
+ cur->alines->expanded = 0;
+ cur->row_count = 1;
+ cur->cursor_ypos = 0;
+ cur->row_xpos = 0;
+ _formi_init_field_xpos(cur);
+ cur->start_char = 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, row, 1);
+ break;
+
+ case REQ_SCR_BCHAR:
+ _formi_hscroll_back(cur, row, 1);
+ break;
+
+ case REQ_SCR_HFLINE:
+ _formi_hscroll_fwd(cur, row, cur->cols);
+ break;
+
+ case REQ_SCR_HBLINE:
+ _formi_hscroll_back(cur, row, cur->cols);
+ break;
+
+ case REQ_SCR_HFHALF:
+ _formi_hscroll_fwd(cur, row, cur->cols / 2);
+ break;
+
+ case REQ_SCR_HBHALF:
+ _formi_hscroll_back(cur, row, cur->cols / 2);
+ break;
+
+ default:
+ return 0;
+ }
+
+#ifdef DEBUG
+ fprintf(dbg,
+ "exit: cursor_xpos=%d, row_xpos=%d, start_char=%d, length=%d, allocated=%d\n",
+ cur->cursor_xpos, cur->row_xpos, cur->start_char,
+ cur->cur_line->length, cur->cur_line->allocated);
+ fprintf(dbg, "exit: start_line=%p, ypos=%d\n", cur->start_line,
+ cur->cursor_ypos);
+ fprintf(dbg, "exit: string=\"%s\"\n", cur->cur_line->string);
+ assert ((cur->cursor_xpos < INT_MAX) && (cur->row_xpos < INT_MAX)
+ && (cur->cursor_xpos >= cur->row_xpos));
+#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;
+ int ret_val, count;
+
+
+ if ((form == NULL) || (form->fields == NULL) ||
+ (form->fields[0] == NULL))
+ return E_INVALID_FIELD;
+
+ cur = form->fields[form->cur_field];
+
+ /*
+ * Sync the buffer if it has been modified so the field
+ * validation routines can use it and because this is
+ * the correct behaviour according to AT&T implementation.
+ */
+ if ((cur->buf0_status == TRUE)
+ && ((ret_val = _formi_sync_buffer(cur)) != E_OK))
+ return ret_val;
+
+ /*
+ * If buffer is untouched then the string pointer may be
+ * NULL, see if this is ok or not.
+ */
+ if (cur->buffers[0].string == NULL) {
+ if ((cur->opts & O_NULLOK) == O_NULLOK)
+ return E_OK;
+ else
+ return E_INVALID_FIELD;
+ }
+
+ count = _formi_skip_blanks(cur->buffers[0].string, 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 lesser 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,
+ (size_t) (sizeof(FIELD *) * form->field_count));
+ qsort(sort_area, (size_t) 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;
+
+ }
+ }
+}
+
+/*
+ * Calculate the length of the displayed line allowing for any tab
+ * characters that need to be expanded. We assume that the tab stops
+ * are 8 characters apart. The parameters start and end are the
+ * character positions in the string str we want to get the length of,
+ * the function returns the number of characters from the start
+ * position to the end position that should be displayed after any
+ * intervening tabs have been expanded.
+ */
+int
+_formi_tab_expanded_length(char *str, unsigned int start, unsigned int end)
+{
+ int len, start_len, i;
+
+ /* if we have a null string then there is no length */
+ if (str[0] == '\0')
+ return 0;
+
+ len = 0;
+ start_len = 0;
+
+ /*
+ * preceding tabs affect the length tabs in the span, so
+ * we need to calculate the length including the stuff before
+ * start and then subtract off the unwanted bit.
+ */
+ for (i = 0; i <= end; i++) {
+ if (i == start) /* stash preamble length for later */
+ start_len = len;
+
+ if (str[i] == '\0')
+ break;
+
+ if (str[i] == '\t')
+ len = len - (len % 8) + 8;
+ else
+ len++;
+ }
+
+#ifdef DEBUG
+ if (dbg != NULL) {
+ fprintf(dbg,
+ "tab_expanded: start=%d, end=%d, expanded=%d (diff=%d)\n",
+ start, end, (len - start_len), (end - start));
+ }
+#endif
+
+ return (len - start_len);
+}
+
+/*
+ * Calculate the tab stops on a given line in the field and set up
+ * the tabs list with the results. We do this by scanning the line for tab
+ * characters and if one is found, noting the position and the number of
+ * characters to get to the next tab stop. This information is kept to
+ * make manipulating the field (scrolling and so on) easier to handle.
+ */
+void
+_formi_calculate_tabs(_FORMI_FIELD_LINES *row)
+{
+ _formi_tab_t *ts = row->tabs, *old_ts = NULL, **tsp;
+ int i, j;
+
+ /*
+ * If the line already has tabs then invalidate them by
+ * walking the list and killing the in_use flag.
+ */
+ for (; ts != NULL; ts = ts->fwd)
+ ts->in_use = FALSE;
+
+
+ /*
+ * Now look for tabs in the row and record the info...
+ */
+ tsp = &row->tabs;
+ for (i = 0, j = 0; i < row->length; i++, j++) {
+ if (row->string[i] == '\t') {
+ if (*tsp == NULL) {
+ if ((*tsp = (_formi_tab_t *)
+ malloc(sizeof(_formi_tab_t))) == NULL)
+ return;
+ (*tsp)->back = old_ts;
+ (*tsp)->fwd = NULL;
+ }
+
+ (*tsp)->in_use = TRUE;
+ (*tsp)->pos = i;
+ (*tsp)->size = 8 - (j % 8);
+ j += (*tsp)->size - 1;
+ old_ts = *tsp;
+ tsp = &(*tsp)->fwd;
+ }
+ }
+}
+
+/*
+ * Return the size of the tab padding for a tab character at the given
+ * position. Return 1 if there is not a tab char entry matching the
+ * given location.
+ */
+static int
+tab_size(_FORMI_FIELD_LINES *row, unsigned int i)
+{
+ _formi_tab_t *ts;
+
+ ts = row->tabs;
+ while ((ts != NULL) && (ts->pos != i))
+ ts = ts->fwd;
+
+ if (ts == NULL)
+ return 1;
+ else
+ return ts->size;
+}
+
+/*
+ * Find the character offset that corresponds to longest tab expanded
+ * string that will fit into the given window. Walk the string backwards
+ * evaluating the sizes of any tabs that are in the string. Note that
+ * using this function on a multi-line window will produce undefined
+ * results - it is really only required for a single row field.
+ */
+static int
+tab_fit_window(FIELD *field, unsigned int pos, unsigned int window)
+{
+ int scroll_amt, i;
+ _formi_tab_t *ts;
+
+ /* first find the last tab */
+ ts = field->alines->tabs;
+
+ /*
+ * unless there are no tabs - just return the window size,
+ * if there is enough room, otherwise 0.
+ */
+ if (ts == NULL) {
+ if (field->alines->length < window)
+ return 0;
+ else
+ return field->alines->length - window + 1;
+ }
+
+ while ((ts->fwd != NULL) && (ts->fwd->in_use == TRUE))
+ ts = ts->fwd;
+
+ /*
+ * now walk backwards finding the first tab that is to the
+ * left of our starting pos.
+ */
+ while ((ts != NULL) && (ts->in_use == TRUE) && (ts->pos > pos))
+ ts = ts->back;
+
+ scroll_amt = 0;
+ for (i = pos; i >= 0; i--) {
+ if (field->alines->string[i] == '\t') {
+ assert((ts != NULL) && (ts->in_use == TRUE));
+ if (ts->pos == i) {
+ if ((scroll_amt + ts->size) > window) {
+ break;
+ }
+ scroll_amt += ts->size;
+ ts = ts->back;
+ }
+ else
+ assert(ts->pos == i);
+ } else {
+ scroll_amt++;
+ if (scroll_amt > window)
+ break;
+ }
+ }
+
+ return ++i;
+}
+
+/*
+ * Return the position of the last character that will fit into the
+ * given width after tabs have been expanded for a given row of a given
+ * field.
+ */
+static unsigned int
+tab_fit_len(_FORMI_FIELD_LINES *row, unsigned int width)
+{
+ unsigned int pos, len, row_pos;
+ _formi_tab_t *ts;
+
+ ts = row->tabs;
+ pos = 0;
+ len = 0;
+ row_pos = 0;
+
+ if (width == 0)
+ return 0;
+
+ while ((len < width) && (pos < row->length)) {
+ if (row->string[pos] == '\t') {
+ assert((ts != NULL) && (ts->in_use == TRUE));
+ if (ts->pos == row_pos) {
+ if ((len + ts->size) > width)
+ break;
+ len += ts->size;
+ ts = ts->fwd;
+ }
+ else
+ assert(ts->pos == row_pos);
+ } else
+ len++;
+ pos++;
+ row_pos++;
+ }
+
+ if (pos > 0)
+ pos--;
+ return pos;
+}
+
+/*
+ * Sync the field line structures with the contents of buffer 0 for that
+ * field. We do this by walking all the line structures and concatenating
+ * all the strings into one single string in buffer 0.
+ */
+int
+_formi_sync_buffer(FIELD *field)
+{
+ _FORMI_FIELD_LINES *line;
+ char *nstr, *tmp;
+ unsigned length;
+
+ if (field->alines == NULL)
+ return E_BAD_ARGUMENT;
+
+ if (field->alines->string == NULL)
+ return E_BAD_ARGUMENT;
+
+ /*
+ * init nstr up front, just in case there are no line contents,
+ * this could happen if the field just contains hard returns.
+ */
+ if ((nstr = malloc(sizeof(char))) == NULL)
+ return E_SYSTEM_ERROR;
+ nstr[0] = '\0';
+
+ line = field->alines;
+ length = 1; /* allow for terminating null */
+
+ while (line != NULL) {
+ if (line->length != 0) {
+ if ((tmp = realloc(nstr,
+ (size_t) (length + line->length)))
+ == NULL) {
+ if (nstr != NULL)
+ free(nstr);
+ return (E_SYSTEM_ERROR);
+ }
+
+ nstr = tmp;
+ strcat(nstr, line->string);
+ length += line->length;
+ }
+
+ line = line->next;
+ }
+
+ if (field->buffers[0].string != NULL)
+ free(field->buffers[0].string);
+ field->buffers[0].allocated = length;
+ field->buffers[0].length = length - 1;
+ field->buffers[0].string = nstr;
+ return E_OK;
+}
+
+
+
--- /dev/null
+/* $NetBSD: internals.h,v 1.10 2004/11/24 11:57:09 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 <stdio.h>
+#include "form.h"
+
+#ifndef FORMI_INTERNALS_H
+#define FORMI_INTERNALS_H 1
+
+#ifdef DEBUG
+extern FILE *dbg;
+#endif
+
+/* direction definitions for _formi_pos_new_field */
+#define _FORMI_BACKWARD 1
+#define _FORMI_FORWARD 2
+
+/* define the default options for a form... */
+#define DEFAULT_FORM_OPTS (O_VISIBLE | O_ACTIVE | O_PUBLIC | O_EDIT | \
+ O_WRAP | O_BLANK | O_AUTOSKIP | O_NULLOK | \
+ O_PASSOK | O_STATIC)
+
+/* definitions of the flags for the FIELDTYPE structure */
+#define _TYPE_NO_FLAGS 0
+#define _TYPE_HAS_ARGS 0x01
+#define _TYPE_IS_LINKED 0x02
+#define _TYPE_IS_BUILTIN 0x04
+#define _TYPE_HAS_CHOICE 0x08
+
+typedef struct formi_type_link_struct formi_type_link;
+
+struct formi_type_link_struct
+{
+ FIELDTYPE *next;
+ FIELDTYPE *prev;
+};
+
+
+struct _formi_page_struct
+{
+ int in_use;
+ int first;
+ int last;
+ int top_left;
+ int bottom_right;
+};
+
+struct _formi_tab_stops
+{
+ struct _formi_tab_stops *fwd;
+ struct _formi_tab_stops *back;
+ unsigned char in_use;
+ unsigned pos;
+ unsigned size;
+};
+
+typedef struct _formi_tab_stops _formi_tab_t;
+
+/* lines structure for the field - keeps start and ends and length of the
+ * lines in a field.
+ */
+struct _formi_field_lines
+{
+ _FORMI_FIELD_LINES *prev;
+ _FORMI_FIELD_LINES *next;
+ unsigned allocated;
+ unsigned length;
+ unsigned expanded;
+ char *string;
+ unsigned char hard_ret; /* line contains hard return */
+ _formi_tab_t *tabs;
+};
+
+
+/* function prototypes */
+unsigned
+_formi_skip_blanks(char *string, unsigned int start);
+int
+_formi_add_char(FIELD *cur, unsigned pos, char c);
+void
+_formi_calculate_tabs(_FORMI_FIELD_LINES *row);
+int
+_formi_draw_page(FORM *form);
+int
+_formi_find_pages(FORM *form);
+int
+_formi_field_choice(FORM *form, int c);
+void
+_formi_init_field_xpos(FIELD *field);
+int
+_formi_manipulate_field(FORM *form, int c);
+int
+_formi_pos_first_field(FORM *form);
+int
+_formi_pos_new_field(FORM *form, unsigned direction, unsigned use_sorted);
+void
+_formi_redraw_field(FORM *form, int field);
+void
+_formi_sort_fields(FORM *form);
+void
+_formi_stitch_fields(FORM *form);
+int
+_formi_tab_expanded_length(char *str, unsigned int start, unsigned int end);
+int
+_formi_update_field(FORM *form, int old_field);
+int
+_formi_validate_char(FIELD *field, char c);
+int
+_formi_validate_field(FORM *form);
+int
+_formi_wrap_field(FIELD *field, _FORMI_FIELD_LINES *pos);
+int
+_formi_sync_buffer(FIELD *field);
+
+#ifdef DEBUG
+int
+_formi_create_dbg_file(void);
+#endif /* DEBUG */
+
+#endif
+
+
+
--- /dev/null
+/* $NetBSD: post.c,v 1.9 2003/03/09 00:57:19 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1998-2000 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: post.c,v 1.9 2003/03/09 00:57:19 lukem Exp $");
+
+#include "form.h"
+#include "internals.h"
+
+/*
+ * Post the form to the screen.
+ */
+int
+post_form(FORM *form)
+{
+ int rows, cols, status;
+
+ if (form == NULL)
+ return E_BAD_ARGUMENT;
+
+ if (form->posted == 1)
+ return E_POSTED;
+
+ if ((form->fields == NULL) || (form->fields[0] == NULL))
+ return E_NOT_CONNECTED;
+
+ if (form->in_init == 1)
+ return E_BAD_STATE;
+
+ if (scale_form(form, &rows, &cols) != E_OK)
+ return E_SYSTEM_ERROR;
+
+ if ((form->scrwin != NULL) && ((rows > getmaxy(form->scrwin))
+ || (cols > getmaxx(form->scrwin)))) {
+ return E_NO_ROOM;
+ }
+
+#ifdef DEBUG
+ if (_formi_create_dbg_file() != E_OK)
+ return E_SYSTEM_ERROR;
+#endif
+
+ form->in_init = 1;
+ if (form->form_init != NULL)
+ form->form_init(form);
+
+ if (form->field_init != NULL)
+ form->field_init(form);
+ form->in_init = 0;
+
+ _formi_pos_first_field(form);
+ if ((status = _formi_draw_page(form)) != E_OK)
+ return status;
+
+ form->posted = 1;
+ pos_form_cursor(form);
+
+ return E_OK;
+}
+
+/*
+ * Unpost the form from the screen
+ */
+int
+unpost_form(FORM *form)
+{
+
+ if (form == NULL)
+ return E_BAD_ARGUMENT;
+
+ if (form->posted != 1)
+ return E_NOT_POSTED;
+
+ if (form->in_init == 1)
+ return E_BAD_STATE;
+
+ form->in_init = 1;
+ if (form->field_term != NULL)
+ form->field_term(form);
+
+ if (form->form_term != NULL)
+ form->form_term(form);
+ form->in_init = 0;
+
+ wclear(form->scrwin);
+
+ form->posted = 0;
+
+ return E_OK;
+}
+
--- /dev/null
+# $NetBSD: shlib_version,v 1.17 2009/01/11 03:07:48 christos Exp $
+# Remember to update distrib/sets/lists/base/shl.* when changing
+#
+major=6
+minor=0
--- /dev/null
+/* $NetBSD: std_header.c,v 1.3 2003/03/09 00:57:19 lukem 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: std_header.c,v 1.3 2003/03/09 00:57:19 lukem Exp $");
+
--- /dev/null
+/* $NetBSD: type_alnum.c,v 1.10 2004/11/24 11:57:09 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 <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include "form.h"
+#include "internals.h"
+
+/*
+ * The alpha-numeric type handling.
+ */
+
+typedef struct
+{
+ unsigned width;
+} alnum_args;
+
+/*
+ * Create the alnum arguments structure from the given args. Return NULL
+ * if the call fails, otherwise return a pointer to the structure allocated.
+ */
+static char *
+create_alnum_args(va_list *args)
+{
+ alnum_args *new;
+
+ new = (alnum_args *) malloc(sizeof(alnum_args));
+
+ if (new != NULL)
+ new->width = va_arg(*args, int);
+
+ return (void *) new;
+}
+
+/*
+ * Copy the alnum argument structure.
+ */
+static char *
+copy_alnum_args(char *args)
+{
+ alnum_args *new;
+
+ new = (alnum_args *) malloc(sizeof(alnum_args));
+
+ if (new != NULL)
+ new->width = ((alnum_args *) (void *)args)->width;
+
+ return (char *) (void *) new;
+}
+
+/*
+ * Free the allocated storage associated with the type arguments.
+ */
+static void
+free_alnum_args(char *args)
+{
+ if (args != NULL)
+ free(args);
+}
+
+/*
+ * Check the contents of the field buffer are alphanumeric only.
+ */
+static int
+alnum_check_field(FIELD *field, char *args)
+{
+ int width, start, cur, end;
+ char *buf, *new;
+
+ width = ((alnum_args *) (void *) field->args)->width;
+ buf = args;
+ start = 0;
+
+ if (buf == NULL)
+ return FALSE;
+
+ /* skip leading white space */
+ while ((buf[start] != '\0')
+ && ((buf[start] == ' ') || (buf[start] == '\t')))
+ start++;
+
+ /* no good if we have hit the end */
+ if (buf[start] == '\0')
+ return FALSE;
+
+ /* find the end of the non-whitespace stuff */
+ cur = start;
+ while(isalnum((unsigned char)buf[cur]))
+ cur++;
+
+ /* no good if it exceeds the width */
+ if ((cur - start) > width)
+ return FALSE;
+
+ end = cur;
+
+ /* check there is only trailing whitespace */
+ while ((buf[cur] != '\0')
+ && ((buf[cur] == ' ') || (buf[cur] == '\t')))
+ cur++;
+
+ /* no good if we are not at the end of the string */
+ if (buf[cur] != '\0')
+ return FALSE;
+
+ if ((new = (char *) malloc(sizeof(char) * (end - start))) == NULL)
+ return FALSE;
+
+ if ((end - start) >= 1) {
+ strncpy(new, &buf[start], (size_t) (end - start - 1));
+ new[end] = '\0';
+ } else
+ new[0]= '\0';
+
+ set_field_buffer(field, 0, new);
+ free(new);
+
+ /* otherwise all was ok */
+ return TRUE;
+}
+
+/*
+ * Check the given character is alpha-numeric, return TRUE if it is.
+ */
+static int
+alnum_check_char(/* ARGSUSED1 */ int c, char *args)
+{
+ return (isalnum(c) ? TRUE : FALSE);
+}
+
+static FIELDTYPE builtin_alnum = {
+ _TYPE_HAS_ARGS | _TYPE_IS_BUILTIN, /* flags */
+ 0, /* refcount */
+ NULL, /* link */
+ create_alnum_args, /* make_args */
+ copy_alnum_args, /* copy_args */
+ free_alnum_args, /* free_args */
+ alnum_check_field, /* field_check */
+ alnum_check_char, /* char_check */
+ NULL, /* next_choice */
+ NULL /* prev_choice */
+};
+
+FIELDTYPE *TYPE_ALNUM = &builtin_alnum;
+
+
--- /dev/null
+/* $NetBSD: type_alpha.c,v 1.11 2004/11/24 11:57:09 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 <sys/cdefs.h>
+__RCSID("$NetBSD: type_alpha.c,v 1.11 2004/11/24 11:57:09 blymn Exp $");
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "form.h"
+#include "internals.h"
+
+/*
+ * The alpha type handling.
+ */
+
+typedef struct
+{
+ unsigned width;
+} alpha_args;
+
+/*
+ * Create the alpha arguments structure from the given args. Return NULL
+ * if the call fails, otherwise return a pointer to the structure allocated.
+ */
+static char *
+create_alpha_args(va_list *args)
+{
+ alpha_args *new;
+
+ new = (alpha_args *) malloc(sizeof(alpha_args));
+
+ if (new != NULL)
+ new->width = va_arg(*args, int);
+
+ return (void *) new;
+}
+
+/*
+ * Copy the alpha argument structure.
+ */
+static char *
+copy_alpha_args(char *args)
+{
+ alpha_args *new;
+
+ new = (alpha_args *) malloc(sizeof(alpha_args));
+
+ if (new != NULL)
+ new->width = ((alpha_args *) (void *) args)->width;
+
+ return (void *) new;
+}
+
+/*
+ * Free the allocated storage associated with the type arguments.
+ */
+static void
+free_alpha_args(char *args)
+{
+ if (args != NULL)
+ free(args);
+}
+
+/*
+ * Check the contents of the field buffer are alphanumeric only.
+ */
+static int
+alpha_check_field(FIELD *field, char *args)
+{
+ int width, start, cur, end;
+ char *buf, *new;
+
+ width = ((alpha_args *) (void *) field->args)->width;
+ buf = args;
+ start = 0;
+
+ if (buf == NULL)
+ return FALSE;
+
+ /* skip leading white space */
+ while ((buf[start] != '\0')
+ && ((buf[start] == ' ') || (buf[start] == '\t')))
+ start++;
+
+ /* no good if we have hit the end */
+ if (buf[start] == '\0')
+ return FALSE;
+
+ /* find the end of the non-whitespace stuff */
+ cur = start;
+ while(isalpha((unsigned char)buf[cur]))
+ cur++;
+
+ /* no good if it exceeds the width */
+ if ((cur - start) > width)
+ return FALSE;
+
+ end = cur;
+
+ /* check there is only trailing whitespace */
+ while ((buf[cur] != '\0')
+ && ((buf[cur] == ' ') || (buf[cur] == '\t')))
+ cur++;
+
+ /* no good if we are not at the end of the string */
+ if (buf[cur] != '\0')
+ return FALSE;
+
+ /* set buffer 0 to the new string */
+ if ((new = (char *) malloc(sizeof(char) * (end - start))) == NULL)
+ return FALSE;
+
+ if ((end - start) >= 1) {
+ strncpy(new, &buf[start], (size_t) (end - start - 1));
+ new[end] = '\0';
+ } else
+ new[0] = '\0';
+
+
+ set_field_buffer(field, 0, new);
+ free(new);
+
+ /* otherwise all was ok */
+ return TRUE;
+}
+
+/*
+ * Check the given character is alphabetic, return TRUE if it is.
+ */
+static int
+alpha_check_char(/* ARGSUSED1 */ int c, char *args)
+{
+ return (isalpha(c) ? TRUE : FALSE);
+}
+
+static FIELDTYPE builtin_alpha = {
+ _TYPE_HAS_ARGS | _TYPE_IS_BUILTIN, /* flags */
+ 0, /* refcount */
+ NULL, /* link */
+ create_alpha_args, /* make_args */
+ copy_alpha_args, /* copy_args */
+ free_alpha_args, /* free_args */
+ alpha_check_field, /* field_check */
+ alpha_check_char, /* char_check */
+ NULL, /* next_choice */
+ NULL /* prev_choice */
+};
+
+FIELDTYPE *TYPE_ALPHA = &builtin_alpha;
+
+
--- /dev/null
+/* $NetBSD: type_enum.c,v 1.11 2010/05/13 17:52:12 tnozaki 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: type_enum.c,v 1.11 2010/05/13 17:52:12 tnozaki Exp $");
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <strings.h>
+#include "form.h"
+#include "internals.h"
+
+/*
+ * Prototypes.
+ */
+static int
+trim_blanks(char *field);
+
+/*
+ * The enum type handling.
+ */
+
+typedef struct
+{
+ char **choices;
+ unsigned num_choices;
+ bool ignore_case;
+ bool exact;
+} enum_args;
+
+/*
+ * Find the first non-blank character at the end of a field, return the
+ * index of that character.
+ */
+static int
+trim_blanks(char *field)
+{
+ int i;
+
+ i = (int) strlen(field);
+ if (i > 0)
+ i--;
+ else
+ return 0;
+
+ while ((i > 0) && isblank((unsigned char)field[i]))
+ i--;
+
+ return i;
+}
+
+/*
+ * Create the enum arguments structure from the given args. Return NULL
+ * if the call fails, otherwise return a pointer to the structure allocated.
+ */
+static char *
+create_enum_args(va_list *args)
+{
+ enum_args *new;
+ char **choices;
+
+ new = (enum_args *) malloc(sizeof(enum_args));
+
+ if (new != NULL) {
+ new->choices = va_arg(*args, char **);
+ new->ignore_case = (va_arg(*args, int)) ? TRUE : FALSE;
+ new->exact = (va_arg(*args, int)) ? TRUE : FALSE;
+
+#ifdef DEBUG
+ if (_formi_create_dbg_file() != E_OK)
+ return NULL;
+ fprintf(dbg,
+ "create_enum_args: ignore_case %d, no_blanks %d\n",
+ new->ignore_case, new->exact);
+#endif
+
+ /* count the choices we have */
+ choices = new->choices;
+ new->num_choices = 0;
+ while (*choices != NULL) {
+#ifdef DEBUG
+ fprintf(dbg, "create_enum_args: choice[%d] = \'%s\'\n",
+ new->num_choices,
+ new->choices[new->num_choices]);
+#endif
+ new->num_choices++;
+ choices++;
+ }
+#ifdef DEBUG
+ fprintf(dbg, "create_enum_args: have %d choices\n",
+ new->num_choices);
+#endif
+
+ }
+
+ return (void *) new;
+}
+
+/*
+ * Copy the enum argument structure.
+ */
+static char *
+copy_enum_args(char *args)
+{
+ enum_args *new;
+
+ new = (enum_args *) malloc(sizeof(enum_args));
+
+ if (new != NULL)
+ bcopy(args, new, sizeof(enum_args));
+
+ return (void *) new;
+}
+
+/*
+ * Free the allocated storage associated with the type arguments.
+ */
+static void
+free_enum_args(char *args)
+{
+ if (args != NULL)
+ free(args);
+}
+
+/*
+ * Attempt to match the string in this to the choices given. Returns
+ * TRUE if match found otherwise FALSE.
+ *
+ */
+static bool
+match_enum(char **choices, unsigned num_choices, bool ignore_case,
+ bool exact, char *this, unsigned *match_num)
+{
+ unsigned i, start, end, enum_start, blen, elen, enum_end;
+ bool cur_match;
+
+ start = _formi_skip_blanks(this, 0);
+ end = trim_blanks(this);
+
+ if (end >= start)
+ blen = (unsigned) (strlen(&this[start])
+ - strlen(&this[end]) + 1);
+ else
+ blen = 0;
+
+#ifdef DEBUG
+ fprintf(dbg, "match_enum: start %d, blen %d\n", start, blen);
+#endif
+ for (i = 0; i < num_choices; i++) {
+ enum_start = _formi_skip_blanks(choices[i], 0);
+ enum_end = trim_blanks(choices[i]);
+
+ if (enum_end >= enum_start)
+ elen = (unsigned) (strlen(&choices[i][enum_start])
+ - strlen(&choices[i][enum_end]) + 1);
+ else
+ elen = 0;
+
+#ifdef DEBUG
+ fprintf(dbg, "match_enum: checking choice \'%s\'\n",
+ choices[i]);
+ fprintf(dbg, "match_enum: enum_start %d, elen %d\n",
+ enum_start, elen);
+#endif
+
+ /* don't bother if we are after an exact match
+ * and the test length is not equal to the enum
+ * in question - it will never match.
+ */
+ if ((exact == TRUE) && (blen != elen))
+ continue;
+
+ /*
+ * If the test length is longer than the enum
+ * length then there is no chance of a match
+ * so we skip.
+ */
+ if ((exact != TRUE) && (blen > elen))
+ continue;
+
+ if (ignore_case)
+ cur_match = (strncasecmp(&choices[i][enum_start],
+ &this[start],
+ (size_t)blen) == 0) ?
+ TRUE : FALSE;
+ else
+ cur_match = (strncmp(&choices[i][enum_start],
+ &this[start],
+ (size_t) blen) == 0) ?
+ TRUE : FALSE;
+
+#ifdef DEBUG
+ fprintf(dbg, "match_enum: curmatch is %s\n",
+ (cur_match == TRUE)? "TRUE" : "FALSE");
+#endif
+
+ if (cur_match == TRUE) {
+ *match_num = i;
+ return TRUE;
+ }
+
+ }
+
+#ifdef DEBUG
+ fprintf(dbg, "match_enum: no match found\n");
+#endif
+ return FALSE;
+}
+
+/*
+ * Check the contents of the field buffer match one of the enum strings only.
+ */
+static int
+enum_check_field(FIELD *field, char *args)
+{
+ enum_args *ta;
+ unsigned match_num;
+
+ if (args == NULL)
+ return FALSE;
+
+ ta = (enum_args *) (void *) field->args;
+
+ if (match_enum(ta->choices, ta->num_choices, ta->ignore_case,
+ ta->exact, args, &match_num) == TRUE) {
+#ifdef DEBUG
+ fprintf(dbg, "enum_check_field: We matched, match_num %d\n",
+ match_num);
+ fprintf(dbg, "enum_check_field: buffer is \'%s\'\n",
+ ta->choices[match_num]);
+#endif
+ set_field_buffer(field, 0, ta->choices[match_num]);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * Get the next enum in the list of choices.
+ */
+static int
+next_enum(FIELD *field, char *args)
+{
+ enum_args *ta;
+ unsigned cur_choice;
+
+ if (args == NULL)
+ return FALSE;
+
+ ta = (enum_args *) (void *) field->args;
+
+#ifdef DEBUG
+ fprintf(dbg, "next_enum: attempt to match \'%s\'\n", args);
+#endif
+
+ if (match_enum(ta->choices, ta->num_choices, ta->ignore_case,
+ ta->exact, args, &cur_choice) == FALSE) {
+#ifdef DEBUG
+ fprintf(dbg, "next_enum: match failed\n");
+#endif
+ return FALSE;
+ }
+
+#ifdef DEBUG
+ fprintf(dbg, "next_enum: cur_choice is %d\n", cur_choice);
+#endif
+
+ cur_choice++;
+
+ if (cur_choice >= ta->num_choices)
+ cur_choice = 0;
+
+#ifdef DEBUG
+ fprintf(dbg, "next_enum: cur_choice is %d on exit\n",
+ cur_choice);
+#endif
+
+ set_field_buffer(field, 0, ta->choices[cur_choice]);
+ return TRUE;
+}
+
+/*
+ * Get the previous enum in the list of choices.
+ */
+static int
+prev_enum(FIELD *field, char *args)
+{
+ enum_args *ta;
+ unsigned cur_choice;
+
+ if (args == NULL)
+ return FALSE;
+
+ ta = (enum_args *) (void *) field->args;
+
+#ifdef DEBUG
+ fprintf(dbg, "prev_enum: attempt to match \'%s\'\n", args);
+#endif
+
+ if (match_enum(ta->choices, ta->num_choices, ta->ignore_case,
+ ta->exact, args, &cur_choice) == FALSE) {
+#ifdef DEBUG
+ fprintf(dbg, "prev_enum: match failed\n");
+#endif
+ return FALSE;
+ }
+
+#ifdef DEBUG
+ fprintf(dbg, "prev_enum: cur_choice is %d\n", cur_choice);
+#endif
+ if (cur_choice == 0)
+ cur_choice = ta->num_choices - 1;
+ else
+ cur_choice--;
+
+#ifdef DEBUG
+ fprintf(dbg, "prev_enum: cur_choice is %d on exit\n", cur_choice);
+#endif
+
+ set_field_buffer(field, 0, ta->choices[cur_choice]);
+ return TRUE;
+}
+
+
+static FIELDTYPE builtin_enum = {
+ _TYPE_HAS_ARGS | _TYPE_IS_BUILTIN, /* flags */
+ 0, /* refcount */
+ NULL, /* link */
+ create_enum_args, /* make_args */
+ copy_enum_args, /* copy_args */
+ free_enum_args, /* free_args */
+ enum_check_field, /* field_check */
+ NULL, /* char_check */
+ next_enum, /* next_choice */
+ prev_enum /* prev_choice */
+};
+
+FIELDTYPE *TYPE_ENUM = &builtin_enum;
+
+
--- /dev/null
+/* $NetBSD: type_integer.c,v 1.8 2004/10/28 21:14:52 dsl 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: type_integer.c,v 1.8 2004/10/28 21:14:52 dsl Exp $");
+
+#include <stdlib.h>
+#include <strings.h>
+#include <ctype.h>
+#include "form.h"
+#include "internals.h"
+
+/*
+ * The integer type handling.
+ */
+
+typedef struct
+{
+ unsigned precision;
+ long min;
+ long max;
+} integer_args;
+
+/*
+ * Create the integer arguments structure from the given args. Return NULL
+ * if the call fails, otherwise return a pointer to the structure allocated.
+ */
+static char *
+create_integer_args(va_list *args)
+{
+ integer_args *new;
+
+ new = (integer_args *) malloc(sizeof(integer_args));
+
+ if (new != NULL) {
+ new->precision = va_arg(*args, unsigned);
+ new->min = va_arg(*args, long);
+ new->max = va_arg(*args, long);
+ }
+
+ return (void *) new;
+}
+
+/*
+ * Copy the integer argument structure.
+ */
+static char *
+copy_integer_args(char *args)
+{
+ integer_args *new;
+
+ new = (integer_args *) malloc(sizeof(integer_args));
+
+ if (new != NULL)
+ bcopy(args, new, sizeof(integer_args));
+
+ return (void *) new;
+}
+
+/*
+ * Free the allocated storage associated with the type arguments.
+ */
+static void
+free_integer_args(char *args)
+{
+ if (args != NULL)
+ free(args);
+}
+
+/*
+ * Check the contents of the field buffer are digits only.
+ */
+static int
+integer_check_field(FIELD *field, char *args)
+{
+ int cur;
+ long number, max, min;
+ int precision;
+ char *buf, *new_buf;
+
+ if (args == NULL)
+ return FALSE;
+
+ precision = ((integer_args *) (void *) field->args)->precision;
+ min = ((integer_args *) (void *) field->args)->min;
+ max = ((integer_args *) (void *) field->args)->max;
+
+ buf = args;
+ cur = 0;
+
+ /* skip leading white space */
+ while ((buf[cur] != '\0')
+ && ((buf[cur] == ' ') || (buf[cur] == '\t')))
+ cur++;
+
+ /* no good if we have hit the end */
+ if (buf[cur] == '\0')
+ return FALSE;
+
+ /* find the end of the digits but allow a leading + or - sign */
+ if ((buf[cur] == '-') || (buf[cur] == '+'))
+ cur++;
+
+ while(isdigit((unsigned char)buf[cur]))
+ cur++;
+
+ /* check there is only trailing whitespace */
+ while ((buf[cur] != '\0')
+ && ((buf[cur] == ' ') || (buf[cur] == '\t')))
+ cur++;
+
+ /* no good if we are not at the end of the string */
+ if (buf[cur] != '\0')
+ return FALSE;
+
+ /* convert and range check the number...*/
+ number = atol(buf);
+ if ((min > max) || ((number < min) || (number > max)))
+ return FALSE;
+
+ if (asprintf(&new_buf, "%.*ld", precision, number) < 0)
+ return FALSE;
+
+ /* re-set the field buffer to be the reformatted numeric */
+ set_field_buffer(field, 0, new_buf);
+
+ free(new_buf);
+
+ /* otherwise all was ok */
+ return TRUE;
+}
+
+/*
+ * Check the given character is numeric, return TRUE if it is.
+ */
+static int
+integer_check_char(/* ARGSUSED1 */ int c, char *args)
+{
+ return ((isdigit(c) || (c == '-') || (c == '+')) ? TRUE : FALSE);
+}
+
+static FIELDTYPE builtin_integer = {
+ _TYPE_HAS_ARGS | _TYPE_IS_BUILTIN, /* flags */
+ 0, /* refcount */
+ NULL, /* link */
+ create_integer_args, /* make_args */
+ copy_integer_args, /* copy_args */
+ free_integer_args, /* free_args */
+ integer_check_field, /* field_check */
+ integer_check_char, /* char_check */
+ NULL, /* next_choice */
+ NULL /* prev_choice */
+};
+
+FIELDTYPE *TYPE_INTEGER = &builtin_integer;
+
+
--- /dev/null
+/* $NetBSD: type_ipv4.c,v 1.10 2007/01/17 23:24:22 hubertf 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: type_ipv4.c,v 1.10 2007/01/17 23:24:22 hubertf Exp $");
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include "form.h"
+#include "internals.h"
+
+/*
+ * The IP v4 address type handling.
+ */
+
+/*
+ * define the styles of address we can have, they are:
+ * FORMI_DOTTED_QUAD address of form aaa.bbb.ccc.ddd
+ * FORMI_HEX address of form 0xaabbccdd
+ * FORMI_CLASSLESS address of form aaa.bbb.ccc.ddd/ee
+ */
+#define FORMI_DOTTED_QUAD 0
+#define FORMI_HEX 1
+#define FORMI_CLASSLESS 2
+
+/*
+ * Check the contents of the field buffer are a valid IPv4 address only.
+ */
+static int
+ipv4_check_field(FIELD *field, char *args)
+{
+ char *buf, *buf1, *keeper, *p, *slash;
+ unsigned int vals[4], style, start, mask;
+ unsigned long hex_val, working;
+ int i;
+
+ if (args == NULL)
+ return FALSE;
+
+ if (asprintf(&keeper, "%s", args) < 0)
+ return FALSE;
+
+#ifdef DEBUG
+ fprintf(dbg, "ipv4_check_field: enter with args of %s\n", keeper);
+#endif
+ style = FORMI_DOTTED_QUAD;
+ buf = keeper;
+ hex_val = 0;
+ mask = 0;
+
+ if ((slash = index(buf, '/')) != NULL)
+ style = FORMI_CLASSLESS;
+ else {
+ start = _formi_skip_blanks(buf, 0);
+ if ((buf[start] != '\0') && (buf[start + 1] != '\0') &&
+ (buf[start] == '0') && ((buf[start + 1] == 'x') ||
+ (buf[start + 1] == 'X')))
+ style = FORMI_HEX;
+ }
+
+ switch (style) {
+ case FORMI_CLASSLESS:
+ *slash = '\0';
+ slash++;
+ mask = atoi(slash);
+ if (mask > 32)
+ goto FAIL;
+ /* FALLTHROUGH */
+
+ case FORMI_DOTTED_QUAD:
+ for (i = 0; i < 4; i++) {
+ p = strsep(&buf, ".");
+ if ((p == NULL) || (*p == '\0'))
+ goto FAIL;
+ vals[i] = atoi(p);
+ if (vals[i] > 255)
+ goto FAIL;
+ }
+ break;
+
+
+ case FORMI_HEX:
+ errno = 0;
+ hex_val = strtoul(buf, NULL, 16);
+ if ((hex_val == ULONG_MAX) && (errno == ERANGE))
+ goto FAIL;
+
+ working = hex_val;
+ for (i = 3; i >= 0; i--) {
+ vals[i] = (unsigned int)(working & 0xffUL);
+ working = working >> 8;
+ }
+ break;
+
+ }
+
+ free(keeper);
+
+ buf1 = NULL;
+
+ switch (style) {
+ case FORMI_DOTTED_QUAD:
+ if (asprintf(&buf, "%d.%d.%d.%d", vals[0], vals[1], vals[2],
+ vals[3]) < 0)
+ return FALSE;
+ if (asprintf(&buf1, "%d.%d.%d.%d", vals[0], vals[1],
+ vals[2], vals[3]) < 0)
+ return FALSE;
+ break;
+
+ case FORMI_CLASSLESS:
+ if (asprintf(&buf, "%d.%d.%d.%d/%d", vals[0], vals[1],
+ vals[2], vals[3], mask) < 0)
+ return FALSE;
+ if (asprintf(&buf1, "%d.%d.%d.%d", vals[0], vals[1],
+ vals[2], vals[3]) < 0)
+ return FALSE;
+ break;
+
+ case FORMI_HEX:
+ if (asprintf(&buf, "0x%.8lx", hex_val) < 0)
+ return FALSE;
+ if (asprintf(&buf1, "%d.%d.%d.%d", vals[0], vals[1],
+ vals[2], vals[3]) < 0)
+ return FALSE;
+ break;
+ }
+
+ /* re-set the field buffer to be the reformatted IPv4 address */
+ set_field_buffer(field, 0, buf);
+
+ /*
+ * Set the field buffer 1 to the dotted quad format regardless
+ * of the input format, only if buffer 1 exists.
+ */
+ if (field->nbuf > 1)
+ set_field_buffer(field, 1, buf1);
+
+#ifdef DEBUG
+ fprintf(dbg, "ipv4_check_field: buf0 set to %s\n", buf);
+ fprintf(dbg, "ipv4_check_field: buf1 set to %s\n", buf1);
+#endif
+ free(buf);
+ free(buf1);
+
+ return TRUE;
+
+ /* bail out point if we got a bad entry */
+ FAIL:
+ free(keeper);
+ return FALSE;
+
+}
+
+/*
+ * Check the given character is numeric, return TRUE if it is.
+ */
+static int
+ipv4_check_char(/* ARGSUSED1 */ int c, char *args)
+{
+ return (isxdigit(c) || (c == '.') || (tolower(c) == 'x') ||
+ (c == '/'))? TRUE : FALSE;
+}
+
+static FIELDTYPE builtin_ipv4 = {
+ _TYPE_IS_BUILTIN, /* flags */
+ 0, /* refcount */
+ NULL, /* link */
+ NULL, /* make_args */
+ NULL, /* copy_args */
+ NULL, /* free_args */
+ ipv4_check_field, /* field_check */
+ ipv4_check_char, /* char_check */
+ NULL, /* next_choice */
+ NULL /* prev_choice */
+};
+
+FIELDTYPE *TYPE_IPV4 = &builtin_ipv4;
+
+
--- /dev/null
+/* $NetBSD: type_ipv6.c,v 1.10 2004/11/24 11:57:09 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.
+ *
+ * Many thanks to Jun-ichiro itojun Hagino <itojun@NetBSD.org> for providing
+ * the sample code for the check field function, this function is 99.999%
+ * his code.
+ *
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: type_ipv6.c,v 1.10 2004/11/24 11:57:09 blymn Exp $");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <string.h>
+#include "form.h"
+#include "internals.h"
+
+/*
+ * The IP v6 address type handling.
+ */
+
+/*
+ * Check the contents of the field buffer are a valid Ipv6 address only.
+ */
+static int
+ipv6_check_field(FIELD *field, char *args)
+{
+ char cleaned[NI_MAXHOST];
+ struct addrinfo hints, *res;
+ const int niflags = NI_NUMERICHOST;
+
+ if (args == NULL)
+ return FALSE;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_DGRAM; /* dummy */
+ hints.ai_flags = AI_NUMERICHOST;
+
+ if (getaddrinfo(args, "0", &hints, &res) != 0) {
+ /* no it is not an IPv6 address */
+ return FALSE;
+ }
+
+ if (res->ai_next) {
+ /* somehow the address resolved to multiple
+ * addresses - strange
+ */
+ freeaddrinfo(res);
+ return FALSE;
+ }
+
+ if (getnameinfo(res->ai_addr, res->ai_addrlen, cleaned,
+ (socklen_t) sizeof(cleaned), NULL, 0, niflags) != 0) {
+ freeaddrinfo(res);
+ return FALSE;
+ }
+
+ freeaddrinfo(res);
+
+ /*
+ * now we are sure host is an IPv6 address literal, and "cleaned"
+ * has the uniformly-formatted IPv6 address literal. Re-set the
+ * field buffer to be the reformatted IPv6 address
+ */
+ set_field_buffer(field, 0, cleaned);
+
+ return TRUE;
+}
+
+/*
+ * Check the given character is numeric, return TRUE if it is.
+ */
+static int
+ipv6_check_char(/* ARGSUSED1 */ int c, char *args)
+{
+ return (isxdigit(c) || (c == '.') || (c == ':')) ? TRUE : FALSE;
+}
+
+static FIELDTYPE builtin_ipv6 = {
+ _TYPE_IS_BUILTIN, /* flags */
+ 0, /* refcount */
+ NULL, /* link */
+ NULL, /* make_args */
+ NULL, /* copy_args */
+ NULL, /* free_args */
+ ipv6_check_field, /* field_check */
+ ipv6_check_char, /* char_check */
+ NULL, /* next_choice */
+ NULL /* prev_choice */
+};
+
+FIELDTYPE *TYPE_IPV6 = &builtin_ipv6;
+
+
--- /dev/null
+/* $NetBSD: type_numeric.c,v 1.8 2004/10/28 21:14:52 dsl 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: type_numeric.c,v 1.8 2004/10/28 21:14:52 dsl Exp $");
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "form.h"
+#include "internals.h"
+
+/*
+ * The numeric type handling.
+ */
+
+typedef struct
+{
+ unsigned precision;
+ double min;
+ double max;
+} numeric_args;
+
+/*
+ * Create the numeric arguments structure from the given args. Return NULL
+ * if the call fails, otherwise return a pointer to the structure allocated.
+ */
+static char *
+create_numeric_args(va_list *args)
+{
+ numeric_args *new;
+
+ new = (numeric_args *) malloc(sizeof(numeric_args));
+
+ if (new != NULL) {
+ new->precision = va_arg(*args, unsigned);
+ new->min = va_arg(*args, double);
+ new->max = va_arg(*args, double);
+ }
+
+ return (void *) new;
+}
+
+/*
+ * Copy the numeric argument structure.
+ */
+static char *
+copy_numeric_args(char *args)
+{
+ numeric_args *new;
+
+ new = (numeric_args *) malloc(sizeof(numeric_args));
+
+ if (new != NULL)
+ bcopy(args, new, sizeof(numeric_args));
+
+ return (void *) new;
+}
+
+/*
+ * Free the allocated storage associated with the type arguments.
+ */
+static void
+free_numeric_args(char *args)
+{
+ if (args != NULL)
+ free(args);
+}
+
+/*
+ * Check the contents of the field buffer are numeric only. A valid
+ * number is of the form nnnn[.mmmmm][Ee[+-]ddd]
+ */
+static int
+numeric_check_field(FIELD *field, char *args)
+{
+ int cur;
+ double number, max, min;
+ int precision;
+ char *buf, *new_buf;
+
+ if (args == NULL)
+ return FALSE;
+
+ precision = ((numeric_args *) (void *) field->args)->precision;
+ min = ((numeric_args *) (void *) field->args)->min;
+ max = ((numeric_args *) (void *) field->args)->max;
+
+ buf = args;
+ cur = 0;
+
+ /* skip leading white space */
+ while ((buf[cur] != '\0')
+ && ((buf[cur] == ' ') || (buf[cur] == '\t')))
+ cur++;
+
+ /* no good if we have hit the end */
+ if (buf[cur] == '\0')
+ return FALSE;
+
+ /* find the end of the digits but allow a leading + or - sign, and
+ * a decimal point.
+ */
+ if ((buf[cur] == '-') || (buf[cur] == '+'))
+ cur++;
+
+ while(isdigit((unsigned char)buf[cur]))
+ cur++;
+
+ /* if not at end of string then check for decimal... */
+ if ((buf[cur] != '\0') && (buf[cur] == '.')) {
+ cur++;
+ /* check for more digits now.... */
+ while(isdigit((unsigned char)buf[cur]))
+ cur++;
+ }
+
+ /* check for an exponent */
+ if ((buf[cur] != '\0') &&
+ ((buf[cur] == 'E') || (buf[cur] == 'e'))) {
+ cur++;
+ if (buf[cur] == '\0')
+ return FALSE;
+
+ /* allow a + or a - for exponent */
+ if ((buf[cur] == '+') || (buf[cur] == '-'))
+ cur++;
+
+ if (buf[cur] == '\0')
+ return FALSE;
+
+ /* we expect a digit now */
+ if (!isdigit((unsigned char)buf[cur]))
+ return FALSE;
+
+ /* skip digits for the final time */
+ while(isdigit((unsigned char)buf[cur]))
+ cur++;
+ }
+
+ /* check there is only trailing whitespace */
+ while ((buf[cur] != '\0')
+ && ((buf[cur] == ' ') || (buf[cur] == '\t')))
+ cur++;
+
+ /* no good if we are not at the end of the string */
+ if (buf[cur] != '\0')
+ return FALSE;
+
+ /* convert and range check the number...*/
+ number = atof(buf);
+ if ((min < max) && ((number < min) || (number > max)))
+ return FALSE;
+
+ if (asprintf(&new_buf, "%.*f", precision, number) < 0)
+ return FALSE;
+
+ /* re-set the field buffer to be the reformatted numeric */
+ set_field_buffer(field, 0, new_buf);
+
+ free(new_buf);
+
+ /* otherwise all was ok */
+ return TRUE;
+}
+
+/*
+ * Check the given character is numeric, return TRUE if it is.
+ */
+static int
+numeric_check_char(/* ARGSUSED1 */ int c, char *args)
+{
+ return ((isdigit(c) || (c == '-') || (c == '+')
+ || (c == '.') || (c == 'e') || (c == 'E')) ? TRUE : FALSE);
+}
+
+static FIELDTYPE builtin_numeric = {
+ _TYPE_HAS_ARGS | _TYPE_IS_BUILTIN, /* flags */
+ 0, /* refcount */
+ NULL, /* link */
+ create_numeric_args, /* make_args */
+ copy_numeric_args, /* copy_args */
+ free_numeric_args, /* free_args */
+ numeric_check_field, /* field_check */
+ numeric_check_char, /* char_check */
+ NULL, /* next_choice */
+ NULL /* prev_choice */
+};
+
+FIELDTYPE *TYPE_NUMERIC = &builtin_numeric;
+
+
--- /dev/null
+/* $NetBSD: type_regex.c,v 1.7 2004/11/24 11:57:09 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 <sys/cdefs.h>
+__RCSID("$NetBSD: type_regex.c,v 1.7 2004/11/24 11:57:09 blymn Exp $");
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <regex.h>
+#include "form.h"
+#include "internals.h"
+
+/*
+ * The regex type handling.
+ */
+
+typedef struct
+{
+ regex_t compiled;
+ unsigned references;
+} regex_args;
+
+/*
+ * Create the regex arguments structure from the given args. Return NULL
+ * if the call fails, otherwise return a pointer to the structure allocated.
+ */
+static char *
+create_regex_args(va_list *args)
+{
+ regex_args *new;
+ char *expression;
+
+ new = (regex_args *) malloc(sizeof(regex_args));
+
+ if (new != NULL) {
+ new->references = 1;
+ expression = va_arg(*args, char *);
+ if ((regcomp(&new->compiled, expression,
+ (REG_EXTENDED | REG_NOSUB | REG_NEWLINE))) != 0) {
+ free(new);
+ return NULL;
+ }
+ }
+
+ return (void *) new;
+}
+
+/*
+ * Copy the regex argument structure.
+ */
+static char *
+copy_regex_args(char *args)
+{
+ ((regex_args *) (void *) args)->references++;
+
+ return (void *) args;
+}
+
+/*
+ * Free the allocated storage associated with the type arguments.
+ */
+static void
+free_regex_args(char *args)
+{
+ if (args != NULL) {
+ ((regex_args *) (void *) args)->references--;
+ if (((regex_args *) (void *) args)->references == 0)
+ free(args);
+ }
+}
+
+/*
+ * Check the contents of the field buffer match the regex.
+ */
+static int
+regex_check_field(FIELD *field, char *args)
+{
+ if ((args != NULL) &&
+ (regexec(&((regex_args *) (void *) field->args)->compiled,
+ args, (size_t) 0, NULL, 0) == 0))
+ return TRUE;
+
+ return FALSE;
+}
+
+static FIELDTYPE builtin_regex = {
+ _TYPE_HAS_ARGS | _TYPE_IS_BUILTIN, /* flags */
+ 0, /* refcount */
+ NULL, /* link */
+ create_regex_args, /* make_args */
+ copy_regex_args, /* copy_args */
+ free_regex_args, /* free_args */
+ regex_check_field, /* field_check */
+ NULL, /* char_check */
+ NULL, /* next_choice */
+ NULL /* prev_choice */
+};
+
+FIELDTYPE *TYPE_REGEXP = &builtin_regex;
+
+
2012/10/17 12:00:00,lib/libcrypt
2012/10/17 12:00:00,lib/libcurses
2012/10/17 12:00:00,lib/libedit
+2012/10/17 12:00:00,lib/libform
2012/10/17 12:00:00,lib/libm
2012/10/17 12:00:00,lib/libmenu
2011/09/30 22:08:19,lib/libprop