]> Zhao Yanbai Git Server - minix.git/commitdiff
Importing lib/libform 44/1144/1
authorThomas Cort <tcort@minix3.org>
Tue, 12 Nov 2013 12:56:07 +0000 (07:56 -0500)
committerThomas Cort <tcort@minix3.org>
Tue, 12 Nov 2013 12:56:07 +0000 (07:56 -0500)
No Minix-specific changes needed.

Change-Id: Ia8ddbdb57ac04dfb42d79c374b9e25b189f9dc3b

45 files changed:
distrib/sets/lists/minix/mi
lib/Makefile
lib/libform/Makefile [new file with mode: 0644]
lib/libform/driver.c [new file with mode: 0644]
lib/libform/field.c [new file with mode: 0644]
lib/libform/field_types.c [new file with mode: 0644]
lib/libform/form.c [new file with mode: 0644]
lib/libform/form.h [new file with mode: 0644]
lib/libform/form_cursor.3 [new file with mode: 0644]
lib/libform/form_data.3 [new file with mode: 0644]
lib/libform/form_driver.3 [new file with mode: 0644]
lib/libform/form_field.3 [new file with mode: 0644]
lib/libform/form_field_attributes.3 [new file with mode: 0644]
lib/libform/form_field_buffer.3 [new file with mode: 0644]
lib/libform/form_field_info.3 [new file with mode: 0644]
lib/libform/form_field_just.3 [new file with mode: 0644]
lib/libform/form_field_new.3 [new file with mode: 0644]
lib/libform/form_field_opts.3 [new file with mode: 0644]
lib/libform/form_field_userptr.3 [new file with mode: 0644]
lib/libform/form_field_validation.3 [new file with mode: 0644]
lib/libform/form_fieldtype.3 [new file with mode: 0644]
lib/libform/form_hook.3 [new file with mode: 0644]
lib/libform/form_new.3 [new file with mode: 0644]
lib/libform/form_new_page.3 [new file with mode: 0644]
lib/libform/form_opts.3 [new file with mode: 0644]
lib/libform/form_page.3 [new file with mode: 0644]
lib/libform/form_post.3 [new file with mode: 0644]
lib/libform/form_userptr.3 [new file with mode: 0644]
lib/libform/form_win.3 [new file with mode: 0644]
lib/libform/forms.3 [new file with mode: 0644]
lib/libform/gdbinit [new file with mode: 0644]
lib/libform/internals.c [new file with mode: 0644]
lib/libform/internals.h [new file with mode: 0644]
lib/libform/post.c [new file with mode: 0644]
lib/libform/shlib_version [new file with mode: 0644]
lib/libform/std_header.c [new file with mode: 0644]
lib/libform/type_alnum.c [new file with mode: 0644]
lib/libform/type_alpha.c [new file with mode: 0644]
lib/libform/type_enum.c [new file with mode: 0644]
lib/libform/type_integer.c [new file with mode: 0644]
lib/libform/type_ipv4.c [new file with mode: 0644]
lib/libform/type_ipv6.c [new file with mode: 0644]
lib/libform/type_numeric.c [new file with mode: 0644]
lib/libform/type_regex.c [new file with mode: 0644]
releasetools/nbsd_ports

index caa2f4353e40ca99fe0943a206d35c895fc1a9f4..883401ccb1d810c5aa652f5ec51cb6b34f663402 100644 (file)
 ./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
index f8bcb51b73c65912a5b23a175c7a5cb948d7135d..ee718c68d1c624b17eaedbd1f4ff334c8d388271 100644 (file)
@@ -179,7 +179,7 @@ SUBDIR+=    ../external/bsd/lutok/lib       # depends on lua and libstdc++
 .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")
diff --git a/lib/libform/Makefile b/lib/libform/Makefile
new file mode 100644 (file)
index 0000000..f919518
--- /dev/null
@@ -0,0 +1,84 @@
+#      $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>
diff --git a/lib/libform/driver.c b/lib/libform/driver.c
new file mode 100644 (file)
index 0000000..2dbc029
--- /dev/null
@@ -0,0 +1,467 @@
+/*     $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;
+}
diff --git a/lib/libform/field.c b/lib/libform/field.c
new file mode 100644 (file)
index 0000000..185ed64
--- /dev/null
@@ -0,0 +1,950 @@
+/*     $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;
+}
+
+       
diff --git a/lib/libform/field_types.c b/lib/libform/field_types.c
new file mode 100644 (file)
index 0000000..db7e59a
--- /dev/null
@@ -0,0 +1,274 @@
+/*     $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;
+}
diff --git a/lib/libform/form.c b/lib/libform/form.c
new file mode 100644 (file)
index 0000000..7be3d3c
--- /dev/null
@@ -0,0 +1,613 @@
+/*     $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;
+}
diff --git a/lib/libform/form.h b/lib/libform/form.h
new file mode 100644 (file)
index 0000000..f620950
--- /dev/null
@@ -0,0 +1,382 @@
+/*     $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 */
diff --git a/lib/libform/form_cursor.3 b/lib/libform/form_cursor.3
new file mode 100644 (file)
index 0000000..dbb6a39
--- /dev/null
@@ -0,0 +1,71 @@
+.\"    $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 .
diff --git a/lib/libform/form_data.3 b/lib/libform/form_data.3
new file mode 100644 (file)
index 0000000..3fafd3e
--- /dev/null
@@ -0,0 +1,75 @@
+.\"    $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 .
diff --git a/lib/libform/form_driver.3 b/lib/libform/form_driver.3
new file mode 100644 (file)
index 0000000..3c18991
--- /dev/null
@@ -0,0 +1,226 @@
+.\"    $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 .
diff --git a/lib/libform/form_field.3 b/lib/libform/form_field.3
new file mode 100644 (file)
index 0000000..4432bb2
--- /dev/null
@@ -0,0 +1,111 @@
+.\"    $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 .
diff --git a/lib/libform/form_field_attributes.3 b/lib/libform/form_field_attributes.3
new file mode 100644 (file)
index 0000000..11b1bab
--- /dev/null
@@ -0,0 +1,100 @@
+.\"    $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 .
diff --git a/lib/libform/form_field_buffer.3 b/lib/libform/form_field_buffer.3
new file mode 100644 (file)
index 0000000..7b17104
--- /dev/null
@@ -0,0 +1,134 @@
+.\"    $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.
diff --git a/lib/libform/form_field_info.3 b/lib/libform/form_field_info.3
new file mode 100644 (file)
index 0000000..f299cf3
--- /dev/null
@@ -0,0 +1,89 @@
+.\"    $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 .
diff --git a/lib/libform/form_field_just.3 b/lib/libform/form_field_just.3
new file mode 100644 (file)
index 0000000..e0e0dfb
--- /dev/null
@@ -0,0 +1,97 @@
+.\"    $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 .
diff --git a/lib/libform/form_field_new.3 b/lib/libform/form_field_new.3
new file mode 100644 (file)
index 0000000..f3e4a1c
--- /dev/null
@@ -0,0 +1,117 @@
+.\"    $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 .
diff --git a/lib/libform/form_field_opts.3 b/lib/libform/form_field_opts.3
new file mode 100644 (file)
index 0000000..1de3e2a
--- /dev/null
@@ -0,0 +1,157 @@
+.\"    $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.
diff --git a/lib/libform/form_field_userptr.3 b/lib/libform/form_field_userptr.3
new file mode 100644 (file)
index 0000000..4aff31c
--- /dev/null
@@ -0,0 +1,74 @@
+.\"    $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 .
diff --git a/lib/libform/form_field_validation.3 b/lib/libform/form_field_validation.3
new file mode 100644 (file)
index 0000000..928896e
--- /dev/null
@@ -0,0 +1,82 @@
+.\"    $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 .
diff --git a/lib/libform/form_fieldtype.3 b/lib/libform/form_fieldtype.3
new file mode 100644 (file)
index 0000000..c4faa0b
--- /dev/null
@@ -0,0 +1,150 @@
+.\"    $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 .
diff --git a/lib/libform/form_hook.3 b/lib/libform/form_hook.3
new file mode 100644 (file)
index 0000000..2c70455
--- /dev/null
@@ -0,0 +1,116 @@
+.\"    $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 .
diff --git a/lib/libform/form_new.3 b/lib/libform/form_new.3
new file mode 100644 (file)
index 0000000..7d4b841
--- /dev/null
@@ -0,0 +1,85 @@
+.\"    $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 .
diff --git a/lib/libform/form_new_page.3 b/lib/libform/form_new_page.3
new file mode 100644 (file)
index 0000000..0a1ae42
--- /dev/null
@@ -0,0 +1,75 @@
+.\"    $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 .
diff --git a/lib/libform/form_opts.3 b/lib/libform/form_opts.3
new file mode 100644 (file)
index 0000000..180d160
--- /dev/null
@@ -0,0 +1,104 @@
+.\"    $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 .
diff --git a/lib/libform/form_page.3 b/lib/libform/form_page.3
new file mode 100644 (file)
index 0000000..39a59e3
--- /dev/null
@@ -0,0 +1,119 @@
+.\"    $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.
diff --git a/lib/libform/form_post.3 b/lib/libform/form_post.3
new file mode 100644 (file)
index 0000000..ed71b5f
--- /dev/null
@@ -0,0 +1,85 @@
+.\"    $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 .
diff --git a/lib/libform/form_userptr.3 b/lib/libform/form_userptr.3
new file mode 100644 (file)
index 0000000..0fc4212
--- /dev/null
@@ -0,0 +1,74 @@
+.\"    $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 .
diff --git a/lib/libform/form_win.3 b/lib/libform/form_win.3
new file mode 100644 (file)
index 0000000..0d065f2
--- /dev/null
@@ -0,0 +1,109 @@
+.\"    $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 .
diff --git a/lib/libform/forms.3 b/lib/libform/forms.3
new file mode 100644 (file)
index 0000000..6be7ae3
--- /dev/null
@@ -0,0 +1,235 @@
+.\"    $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
diff --git a/lib/libform/gdbinit b/lib/libform/gdbinit
new file mode 100644 (file)
index 0000000..6a8d423
--- /dev/null
@@ -0,0 +1,20 @@
+#
+# 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
diff --git a/lib/libform/internals.c b/lib/libform/internals.c
new file mode 100644 (file)
index 0000000..e7a68c3
--- /dev/null
@@ -0,0 +1,3651 @@
+/*     $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;
+}
+
+               
+                       
diff --git a/lib/libform/internals.h b/lib/libform/internals.h
new file mode 100644 (file)
index 0000000..52c8ae4
--- /dev/null
@@ -0,0 +1,151 @@
+/*     $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
+
+
+
diff --git a/lib/libform/post.c b/lib/libform/post.c
new file mode 100644 (file)
index 0000000..7fe4085
--- /dev/null
@@ -0,0 +1,119 @@
+/*     $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;
+}
+
diff --git a/lib/libform/shlib_version b/lib/libform/shlib_version
new file mode 100644 (file)
index 0000000..8890efa
--- /dev/null
@@ -0,0 +1,5 @@
+#      $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
diff --git a/lib/libform/std_header.c b/lib/libform/std_header.c
new file mode 100644 (file)
index 0000000..26ff3e3
--- /dev/null
@@ -0,0 +1,34 @@
+/*     $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 $");
+
diff --git a/lib/libform/type_alnum.c b/lib/libform/type_alnum.c
new file mode 100644 (file)
index 0000000..14bc654
--- /dev/null
@@ -0,0 +1,176 @@
+/*     $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;
+
+
diff --git a/lib/libform/type_alpha.c b/lib/libform/type_alpha.c
new file mode 100644 (file)
index 0000000..7f35cc9
--- /dev/null
@@ -0,0 +1,180 @@
+/*     $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;
+
+
diff --git a/lib/libform/type_enum.c b/lib/libform/type_enum.c
new file mode 100644 (file)
index 0000000..1f1b513
--- /dev/null
@@ -0,0 +1,369 @@
+/*     $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;
+
+
diff --git a/lib/libform/type_integer.c b/lib/libform/type_integer.c
new file mode 100644 (file)
index 0000000..64d5be9
--- /dev/null
@@ -0,0 +1,185 @@
+/*     $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;
+
+
diff --git a/lib/libform/type_ipv4.c b/lib/libform/type_ipv4.c
new file mode 100644 (file)
index 0000000..da1dd6a
--- /dev/null
@@ -0,0 +1,211 @@
+/*     $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;
+
+
diff --git a/lib/libform/type_ipv6.c b/lib/libform/type_ipv6.c
new file mode 100644 (file)
index 0000000..cb3b0dd
--- /dev/null
@@ -0,0 +1,123 @@
+/*     $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;
+
+
diff --git a/lib/libform/type_numeric.c b/lib/libform/type_numeric.c
new file mode 100644 (file)
index 0000000..2e24973
--- /dev/null
@@ -0,0 +1,220 @@
+/*     $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;
+
+
diff --git a/lib/libform/type_regex.c b/lib/libform/type_regex.c
new file mode 100644 (file)
index 0000000..866f590
--- /dev/null
@@ -0,0 +1,129 @@
+/*     $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;
+
+
index 8e7c078388bd616d0fa6d961c76248473a4e591a..5082b3f9225f0d918c921e05f9259701ef8a7efc 100644 (file)
@@ -82,6 +82,7 @@
 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