/*      $NetBSD: scanform.c,v 1.43 2005/01/12 17:38:40 peter Exp $       */

/*
 * Copyright (c) 2000 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * Copyright (c) 2000 Tim Rightnour <garbled@NetBSD.org>
 *
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *        This product includes software developed by the NetBSD
 *        Foundation, Inc. and its contributors.
 * 4. Neither the name of The NetBSD Foundation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``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 FOUNDATION OR CONTRIBUTORS
 * 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/param.h>
#include <sys/ioctl.h>
#include <sys/queue.h>
#include <sys/cdefs.h>
#include <sys/stat.h>
#include <sys/wait.h>

#include <ctype.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <curses.h>
#include <form.h>
#include <cdk/cdk.h>

#include "sushi.h"
#include "functions.h"
#include "scanform.h"
#include "formtree.h"
#include "handlers.h"
#include "run.h"
#include "blabel.h"

extern func_record func_map[];
extern CDKSCREEN *cdkscreen;
extern struct winsize ws;
extern nl_catd catalog;
extern char *lang_id;
extern char *keylabel[10];
extern chtype keybinding[10];

static void scan_formindex(struct cqForm *, char *);

static void
form_status(FORM *form)
{
	char buf[20];

	wstandout(stdscr);
	mvwaddstr(stdscr, ws.ws_row - 3, 0, catgets(catalog, 4, 9,
	    "PGUP/PGDN to change page, UP/DOWN switch field, ENTER submit."));
	wstandend(stdscr);
	snprintf(buf, sizeof(buf), "%s (%d/%d)",
	    catgets(catalog, 4, 8, "Form Page:"),
	    form_page(form) + 1, form->max_page); /* XXX */
	mvwaddstr(stdscr, ws.ws_row - 3, 62, buf);
	wrefresh(stdscr);
}

static int
scan_form(struct cqForm *cqf, char *path)
{
	FILE *filep;
	int lcnt;
	char *p, *t;
	size_t len;

	if ((filep = fopen(path, "r")) != NULL) {
		for (lcnt = 1; (p = fgetln(filep, &len)) != NULL; ++lcnt) {
			if (len == 1)		/* Skip empty lines. */
				continue;
			if (p[len - 1] == '#')	/* Skip remarked lines. */
				continue;
			if (p[len - 1] != '\n') {/* Skip corrupted lines. */
				warnx("%s: line %d corrupted", path, lcnt);
				continue;
			}
			p[len - 1] = '\0';	/* Terminate the line. */

						/* Skip leading spaces. */
			for (; *p != '\0' && isspace((unsigned char)*p); ++p)
				continue;
						/* Skip empty/comment lines. */
			if (*p == '\0' || *p == '#')
				continue;
						/* Find first token. */
			for (t = p; *t && !isspace((unsigned char)*t); ++t)
				continue;
			if (*t == '\0')		/* Need more than one token.*/
				continue;
			*t = '\0';

			scan_formindex(cqf, p);
		}

		fclose(filep);
	} else
		return (1);
	return (0);
}

static void
form_appenditem(struct cqForm *cqf, char *desc, int type, char *data, int req)
{
	FTREE_ENTRY *fte;

	if ((fte = malloc(sizeof(FTREE_ENTRY))) == NULL ||
	    ((fte->desc = strdup(desc)) == NULL) ||
	    ((fte->type = type) == 0) ||
	    ((fte->data = strdup(data)) == NULL))
		bailout("malloc: %s", strerror(errno));
	fte->required = req;
	fte->elen = 0;
	fte->origdata = fte->data;
	fte->list = NULL;

	CIRCLEQ_INIT(&fte->cqSubFormHead);
	CIRCLEQ_INSERT_TAIL(cqf, fte, cqFormEntries);
}

static void
scan_formindex(struct cqForm *cqf, char *row)
{
	char *t = row;
	char *x;
	char desc[80];
	int type;
	int req = 0;

	while (*++t && !isspace((unsigned char)*t))
		continue;
	x = (char *)strsep(&row, ":");
	if (strcmp(x, "entry") == 0) {
		type = DATAT_ENTRY;
	} else if (strcmp(x, "req-entry") == 0) {
		type = DATAT_ENTRY;
		req = 1;
	} else if (strcmp(x, "escript") == 0) {
		type = DATAT_ESCRIPT;
	} else if (strcmp(x, "req-escript") == 0) {
		type = DATAT_ESCRIPT;
		req = 1;
	} else if (strcmp(x, "nescript") == 0) {
		type = DATAT_NESCRIPT;
	} else if (strcmp(x, "list") == 0) {
		type = DATAT_LIST;
	} else if (strcmp(x, "multilist") == 0) {
		type = DATAT_MLIST;
	} else if (strcmp(x, "req-list") == 0) {
		type = DATAT_LIST;
		req = 1;
	} else if (strcmp(x, "blank") == 0) {
		type = DATAT_BLANK;
	} else if (strcmp(x, "func") == 0) {
		type = DATAT_FUNC;
	} else if (strcmp(x, "multifunc") == 0) {
		type = DATAT_MFUNC;
	} else if (strcmp(x, "req-func") == 0) {
		type = DATAT_FUNC;
		req = 1;
	} else if (strcmp(x, "script") == 0) {
		type = DATAT_SCRIPT;
	} else if (strcmp(x, "multiscript") == 0) {
		type = DATAT_MSCRIPT;
	} else if (strcmp(x, "req-script") == 0) {
		type = DATAT_SCRIPT;
		req = 1;
	} else if (strcmp(x, "noedit") == 0) {
		type = DATAT_NOEDIT;
	} else if (strcmp(x, "invis") == 0) {
		type = DATAT_INVIS;
	} else if (strcmp(x, "integer") == 0) {
		type = DATAT_INTEGER;
	} else if (strcmp(x, "req-integer") == 0) {
		type = DATAT_INTEGER;
		req = 1;
	} else if (strcmp(x, "iscript") == 0) {
		type = DATAT_ISCRIPT;
	} else if (strcmp(x, "req-iscript") == 0) {
		type = DATAT_ISCRIPT;
		req = 1;
	} else if (strcmp(x, "ipv4") == 0) {
		type = DATAT_V4;
	} else if (strcmp(x, "req-ipv4") == 0) {
		type = DATAT_V4;
		req = 1;
	} else if (strcmp(x, "ipv4script") == 0) {
		type = DATAT_V4SCRIPT;
	} else if (strcmp(x, "req-ipv4script") == 0) {
		type = DATAT_V4SCRIPT;
		req = 1;
	} else if (strcmp(x, "ipv6") == 0) {
		type = DATAT_V6;
	} else if (strcmp(x, "req-ipv6") == 0) {
		type = DATAT_V6;
		req = 1;
	} else if (strcmp(x, "ipv6script") == 0) {
		type = DATAT_V6SCRIPT;
	} else if (strcmp(x, "req-ipv6script") == 0) {
		type = DATAT_V6SCRIPT;
		req = 1;
	} else {
		bailout("%s: %s",
		    catgets(catalog, 1, 11, "invalid data type"), x);
	}

	while (*++t && isspace((unsigned char)*t))
		continue;
	if (strlen(t) > 50)
		bailout("'%s': %s", t,
		    catgets(catalog, 1, 12, "description too long"));

	snprintf(desc, sizeof(desc), "%s", t);
	if (strcmp(desc, "BLANK") == 0)
		snprintf(desc, sizeof(desc), " ");

	form_appenditem(cqf, desc, type, row, req);
}

int
form_entries(struct cqForm *cqf)
{
	FTREE_ENTRY *fte;
	int entries = 0;

	for (fte = CIRCLEQ_FIRST(cqf); fte != (void *)cqf;
	     fte = CIRCLEQ_NEXT(fte, cqFormEntries))
		++entries;

	return (entries);
}

FTREE_ENTRY *
form_getentry(struct cqForm *cqf, int entry)
{
	FTREE_ENTRY *fte;
	int entries = 0;

	for (fte = CIRCLEQ_FIRST(cqf); (fte != (void *)cqf);
	     fte = CIRCLEQ_NEXT(fte, cqFormEntries)) {
		if (entry == entries)
			return (fte);
		++entries;
	}

	return (NULL);
}

void
form_printtree(struct cqForm *cqf)
{
	FTREE_ENTRY *ftp;

	for (ftp = CIRCLEQ_FIRST(cqf); ftp != (void *)cqf;
	     ftp = CIRCLEQ_NEXT(ftp, cqFormEntries))
		printf("%s\t- %d:%s\n", ftp->desc, ftp->type, ftp->data);
}

/* This is the keymap for the forms */
static int 
get_request(WINDOW *w)			/* virtual key mapping */
{
	static int	mode = REQ_INS_MODE;
	int		i, c = wgetch(w);	/* read a character */

	/* printf("GOT A THINGIE: 0x%x %d\n", c, c); */

	/* convert to an FKEY for case */
	if (c != 0)
		for (i = 0; i < 10; i++)
			if (c == keybinding[i])
				c = KEY_F(i+1);

	switch (c) {
	case KEY_F(1):
		return (SHOWHELP);
	case KEY_F(2):
		return (REFRESH);
	case KEY_F(3):
		return (BAIL);
	case KEY_F(4):
		return (GENLIST);
	case KEY_F(5):
		return (RESET);
	case KEY_F(6):
		return (COMMAND);
	case KEY_F(7):
		return (EDIT);
	case KEY_F(8):
		return (DUMPSCREEN);
	case KEY_F(9):
		return (SHELL);
	case KEY_F(10):
		return (FASTBAIL);
	case 0x1b:		/* ESC */
		return (BAIL);
	case 0x0d:
		return (QUIT);
	case KEY_ENTER:
		return (QUIT);
	case 0xa:		/* LF */
		return (QUIT);
	case KEY_NPAGE:		/* ^F */
		return (REQ_NEXT_PAGE);
	case KEY_PPAGE:		/* ^B */
		return (REQ_PREV_PAGE);
	case KEY_DOWN:
		return (REQ_NEXT_FIELD);
	case KEY_UP:
		return (REQ_PREV_FIELD);
	case 0x06:		/* ^F */
		return (REQ_NEXT_PAGE);
	case 0x02:		/* ^B */
		return (REQ_PREV_PAGE);
	case 0x0e:		/* ^N */
		return (REQ_NEXT_FIELD);
	case 0x10:		/* ^P */
		return (REQ_PREV_FIELD);
	case KEY_HOME:
		return (REQ_FIRST_FIELD);
	case KEY_LL:
		return (REQ_LAST_FIELD);
	case 0x0c:		/* ^L */
		return (REQ_LEFT_FIELD);
	case 0x12:		/* ^R */
		return (REQ_RIGHT_FIELD);
	case 0x15:		/* ^U */
		return (REQ_UP_FIELD);
	case 0x04:		/* ^D */
		return (REQ_DOWN_FIELD);
	case 0x17:		/* ^W */
		return (REQ_NEXT_WORD);
	case 0x14:		/* ^T */
		return (REQ_PREV_WORD);
	case 0x01:		/* ^A */
		return (REQ_BEG_FIELD);
	case 0x05:		/* ^E */
		return (REQ_END_FIELD);
	case KEY_LEFT:
		return (REQ_PREV_CHAR);
	case KEY_RIGHT:
		return (REQ_NEXT_CHAR);
	case 0x0f:		/* ^O */
		return (REQ_INS_LINE);
	case KEY_DC:
	case 0x16:		/* ^V */
		return (REQ_DEL_CHAR);
	case KEY_BACKSPACE:
	case 0x08:		/* ^H */
		return (REQ_DEL_PREV);
	case 0x19:		/* ^Y */
		return (REQ_DEL_LINE);
	case 0x07:		/* ^G */
		return (REQ_DEL_WORD);
	case 0x03:		/* ^C */
		return (REQ_CLR_EOL);
	case 0x0b:		/* ^K */
		return (REQ_CLR_EOF);
	case 0x18:		/* ^X */
		return (REQ_CLR_FIELD);
	case 0x9:		/* tab*/
		return (REQ_NEXT_CHOICE);
	case 0x1a:		/* ^Z 0x1a*/
		return (REQ_PREV_CHOICE);
	case KEY_IC:		/* INS */
		if (mode == REQ_INS_MODE)
			return (mode = REQ_OVL_MODE);
		else
			return (mode = REQ_INS_MODE);
	}
	return (c);
}

static int 
my_driver(FORM * form, int c, char *path)
{
	CDKSCROLL *plist;
	CDKSELECTION *slist;
	CDKENTRY *entry;
	FIELD *curfield;
	char **list;
	int i, j, y, n, dcols, drows, dmax;
	char *tmp, *otmp, *p;
	char *choices[] = {" ", "+"};
	char buf[1024];

	switch (c) {
	case EDIT:
		curfield = current_field(form);
		if (field_opts(curfield) & O_STATIC) {
			field_info(curfield, &drows, &dcols, &j, &j, &j, &j);
			dmax = 0;
		} else
			dynamic_field_info(curfield, &drows, &dcols, &dmax);
		if (dmax == 0)
			dmax = dcols;
		entry = newCDKEntry(cdkscreen, BOTTOM, CENTER,
		    catgets(catalog, 4, 6, "Enter the field data "
		    "below, and hit enter to return to the form."),
		    catgets(catalog, 4, 7, "Data Entry: "),
		    A_REVERSE, field_pad(curfield), vMIXED, ws.ws_col - 20,
		    0, dmax, TRUE, FALSE);
		if (field_buffer(curfield, 0) == NULL)
			setCDKEntry(entry, "", 0, dmax, TRUE);
		else
			setCDKEntry(entry, field_buffer(curfield, 0), 0,
			    dmax, TRUE);
		injectCDKEntry(entry, CDK_BEGOFLINE);
		tmp = activateCDKEntry(entry, NULL);
		if (entry->exitType == vESCAPE_HIT) {
			destroyCDKEntry(entry);
			return (FALSE);
		}
		set_field_buffer(curfield, 0, tmp);
		destroyCDKEntry(entry);
		touchwin(stdscr);
		wrefresh(stdscr);
		return (FALSE);
		/* NOTREACHED */
		break;
	case COMMAND:
		/* for now, ignore this.. it's messy */
		return (FALSE);
		/* NOTREACHED */
		break;
	case RESET:
		return (2); /* hrmmm... */
		/* NOTREACHED */
		break;
	case REFRESH:
		touchwin(stdscr);
		wrefresh(stdscr);
		return (FALSE);
		/* NOTREACHED */
		break;
	case SHELL:
		endwin();
		system("/bin/sh");
		wrefresh(stdscr);
		return (FALSE);
		/* NOTREACHED */
		break;
	case DUMPSCREEN:
		do_scrdump(0, NULL, NULL, 0);
		return (FALSE);
		/* NOTREACHED */
		break;
	case SHOWHELP:
		curs_set(0);
		if (simple_lang_handler(path, HELPFILE, handle_help) == -2)
			nohelp();
		touchwin(stdscr);
		wrefresh(stdscr);
		curs_set(1);
		return (FALSE);
		/* NOTREACHED */
		break;
	case GENLIST:
		/* pull the grand popup list */
		curfield = current_field(form);
		list = (char **)field_userptr(curfield);
		if (list == NULL)
			break;
		for (i = 0, y = 0; list[i] != NULL; i++)
			if ((strlen(list[i]) + 1) > y)
				y = strlen(list[i]) + 1;
		if (field_buffer(curfield, 1) == NULL || 
		    field_buffer(curfield, 0) == NULL)
			return (FALSE);
		curs_set(0);
		otmp = tmp = strdup(field_buffer(curfield, 1));
		stripWhiteSpace(vBOTH, tmp);
		if (*tmp == 'm') {
			slist = newCDKSelection(cdkscreen, RIGHT, CENTER,
			    RIGHT, 10, y + 2,
			    catgets(catalog, 4, 1, "Select choices"),
			    list, i, choices, 2, A_REVERSE ,TRUE, FALSE);
			free(otmp);
			otmp = tmp = strdup(field_buffer(curfield, 0));
			stripWhiteSpace(vBOTH, tmp);
			if (*tmp != '\0')
				for (p = tmp; p != NULL; p = strsep(&tmp, ","))
					for (y = 0; y < i; y++)
						if (strcmp(p, list[y]) == 0)
							slist->selections[y] = 1;
			activateCDKSelection(slist, NULL);
			for (y = 0; y < i; y++)
				if (slist->selections[y] == 1) {
					memset(&buf, '\0', sizeof(buf));
					break;
				}
			for (y = 0, n = 0; y < i; y++)
				if (slist->selections[y] == 1) {
					if (++n != 1)
						snprintf(buf, sizeof(buf),
						    "%s,", buf);
					snprintf(buf, sizeof(buf), "%s%s",
					    buf, list[y]);
				}
			free(otmp);
			tmp = buf;
			if (n)
				set_field_buffer(curfield, 0, tmp);
			destroyCDKSelection(slist);
		} else if (*tmp ==  's') {
			plist = newCDKScroll(cdkscreen, RIGHT, CENTER, RIGHT,
			    10, y + 2, catgets(catalog, 4, 1, "Select choice"),
			    list, i, FALSE, A_REVERSE ,TRUE, FALSE);
			i = activateCDKScroll(plist, NULL);
			if (i != -1)
				set_field_buffer(curfield, 0, list[i]);
			destroyCDKScroll(plist);
			free(otmp);
		}
		touchwin(stdscr);
		wrefresh(stdscr);
		curs_set(1);
		return (FALSE);
		/* NOTREACHED */
		break;
	case QUIT:
		/* do something useful */
		if (form_driver(form, REQ_VALIDATION) == E_OK) {
			if ((i = process_form(form, path)) == 0)
				return (TRUE);
			else if (i == -1)
				return (FALSE);
			else
				return (2); /* special meaning */
		}
		/* NOTREACHED */
		break;
	case BAIL:
		return (3); /* TRUE, so handle_preform can free F */
		/* NOTREACHED */
		break;
	case FASTBAIL:
		endwin();
		exit(EXIT_SUCCESS);
		break;
	}
	beep();
	return (FALSE);
}

static FIELD *
LABEL(FIELD_RECORD *x)
{
	char *tmp;
	FIELD *f = new_field(1, (int)(strlen(x->v)+2), x->frow, x->fcol, 0, 0);
	size_t l;

	if (f != NULL) {
		tmp = realloc(x->v, sizeof(char *) * (strlen(x->v) + 3));
		if (tmp == NULL)
			bailout("realloc: %s", strerror(errno));
		x->v = tmp;
		l = strlen(x->v);
		tmp = malloc(sizeof(char *) * l);
		if (tmp == NULL)
			bailout("malloc: %s", strerror(errno));

		if (x->required == 1)
			(void)strncpy(tmp, "* ", l);
		else
			(void)strncpy(tmp, "  ", l);
		(void)strncat(tmp, x->v, l);
		(void)strlcpy(x->v, tmp, l + 3);
		set_field_buffer(f, 0, x->v);
		free(tmp);
		field_opts_off(f, O_ACTIVE);
		if (x->newpage == 1)
			set_new_page(f, TRUE);
	}
	return (f);
}

static FIELD *
STRING(FIELD_RECORD *x)
{				/* create a STRING field */
	FIELD *f = new_field(x->rows, x->cols, x->frow, x->fcol, 0, 0);

	if (f != NULL) {
		set_field_back(f, A_UNDERLINE);
		if (x->newpage == 1)
			set_new_page(f, TRUE);
		if (x->required == 1)
			field_opts_off(f, O_NULLOK);
		if (x->bigfield) {
			field_opts_off(f, O_STATIC);
			set_max_field(f, x->rcols);
		}
		set_field_buffer(f, 0, x->v);
	}
	return (f);
}

static FIELD *
IPV4(FIELD_RECORD *x)
{				/* create an IPV4 field */
	FIELD *f = new_field(x->rows, x->cols, x->frow, x->fcol, 0, 0);

	if (f != NULL) {
		set_field_type(f, TYPE_IPV4);
		set_field_back(f, A_UNDERLINE);
		if (x->newpage == 1)
			set_new_page(f, TRUE);
		if (x->required == 1)
			field_opts_off(f, O_NULLOK);
		if (x->bigfield) {
			field_opts_off(f, O_STATIC);
			set_max_field(f, x->rcols);
		}
		set_field_buffer(f, 0, x->v);
	}
	return (f);
}

static FIELD *
IPV6(FIELD_RECORD *x)
{				/* create an IPV6 field */
	FIELD *f = new_field(x->rows, x->cols, x->frow, x->fcol, 0, 0);

	if (f != NULL) {
		set_field_type(f, TYPE_IPV6);
		set_field_back(f, A_UNDERLINE);
		if (x->newpage == 1)
			set_new_page(f, TRUE);
		if (x->required == 1)
			field_opts_off(f, O_NULLOK);
		if (x->bigfield) {
			field_opts_off(f, O_STATIC);
			set_max_field(f, x->rcols);
		}
		set_field_buffer(f, 0, x->v);
	}
	return (f);
}

static FIELD *
MULTI(FIELD_RECORD *x)
{				/* create a MULTI field */
	FIELD *f = new_field(x->rows, x->cols, x->frow, x->fcol, 0, 1);

	if (f != NULL) {
		set_field_back(f, A_REVERSE);
		if (x->newpage == 1)
			set_new_page(f, TRUE);
		field_opts_off(f, O_STATIC);
		field_opts_off(f, O_EDIT);
		set_field_userptr(f, x->list);
		set_field_buffer(f, 1, "multi");
	}
	return (f);
}

static FIELD *
INTEGER(FIELD_RECORD *x)
{				/* create an INTEGER field */
	FIELD *f = new_field(x->rows, x->cols, x->frow, x->fcol, 0, 0);
	int pre, min, max;
	char *p, *q, *n;

	p = x->v;
	q = strsep(&p, ",");
	pre = atoi(q);
	q = strsep(&p, ",");
	min = atoi(q);
	n = strdup(p);
	q = strsep(&p, ",");
	if (p == NULL)
		max = atoi(n);
	else
		max = atoi(q);

	if (f != NULL) {
		set_field_back(f, A_UNDERLINE);
		set_field_type(f, TYPE_INTEGER, pre, min, max);
		if (x->newpage == 1)
			set_new_page(f, TRUE);
		if (x->required == 1)
			field_opts_off(f, O_NULLOK);
		if (x->bigfield)
			field_opts_off(f, O_STATIC);
		if (p != NULL)
			set_field_buffer(f, 0, p);
	}
	return (f);
}

static FIELD *
INVIS(FIELD_RECORD *x)
{				/* create an INVIS field */
	FIELD *f = new_field(x->rows, x->cols, x->frow, x->fcol, 0, 0);

	if (f != NULL) {
		set_field_opts(f, field_opts(f) & ~(O_ACTIVE|O_VISIBLE));
		if (x->bigfield) {
			field_opts_off(f, O_STATIC);
			set_max_field(f, x->rcols);
		}
		if (x->newpage == 1)
			set_new_page(f, TRUE);
		set_field_buffer(f, 0, x->v);
	}
	return (f);
}

static FIELD *
NOEDIT(FIELD_RECORD *x)
{				/* create a NOEDIT field */
	FIELD *f = new_field(x->rows, x->cols, x->frow, x->fcol, 0, 0);

	if (f != NULL) {
		set_field_opts(f, field_opts(f) & ~(O_EDIT|O_ACTIVE));
		if (x->newpage == 1)
			set_new_page(f, TRUE);
		if (x->bigfield) {
			field_opts_off(f, O_STATIC);
			set_max_field(f, x->rcols);
		}
		set_field_buffer(f, 0, x->v);
	}
	return (f);
}

static FIELD *
ENUM(FIELD_RECORD *x)
{				/* create a ENUM field */
	FIELD *f = new_field(x->rows, x->cols, x->frow, x->fcol, 0, 1);

	if (f != NULL) {
		set_field_back(f, A_REVERSE);
		set_field_type(f, TYPE_ENUM, x->list, FALSE, TRUE);
		set_field_userptr(f, x->list);
		if (x->newpage == 1)
			set_new_page(f, TRUE);
		if (x->required == 1)
			field_opts_off(f, O_NULLOK);
		field_opts_off(f, O_EDIT);
		set_field_buffer(f, 1, "single");
		if (x->bigfield)
			field_opts_off(f, O_STATIC);
		set_field_buffer(f, 0, x->list[0]);
	}
	return (f);
}

static FIELD_RECORD *F;

static int
process_preform(FORM *form, char *path)
{
	char file[PATH_MAX];
	struct stat sb;
	char *p;
	int fc, lcnt, i, j;
	FIELD **f;
	char **args, **nargs;

	if (lang_id == NULL) {
		snprintf(file, sizeof(file), "%s/%s", path, FORMFILE);
	} else {
		snprintf(file, sizeof(file), "%s/%s.%s", path, FORMFILE,
		    lang_id);
		if (stat(file, &sb) != 0)
			snprintf(file, sizeof(file), "%s/%s", path, FORMFILE);
	}

	args = malloc(sizeof(char *) * 2);
	if (args == NULL)
		bailout("malloc: %s", strerror(errno));
	fc = lcnt = field_count(form);
	nargs = realloc(args, sizeof(char *) * (lcnt + 1));
	if (nargs == NULL)
		bailout("realloc: %s", strerror(errno));
	args = nargs;

	f = form_fields(form);
	for (lcnt = 0, i = 0; lcnt < fc; lcnt++) {
		if (F[lcnt].type != (PF_field)LABEL) {
			if (field_buffer(f[lcnt], 0) == NULL)
				args[i] = "";
			else
				args[i] = strdup(field_buffer(f[lcnt], 0));
			if (args[i] != NULL) {
				p = &args[i][strlen(args[i]) - 1];
				while (isspace((unsigned char)*p))
					*p-- = '\0';
			}
			i++;
		}
	}
	args[i] = NULL;

	for (i = 0; F[i].type != NULL; i++) {
		free(F[i].v);
		if (F[i].list != NULL) {
			for (j = 0; F[i].list[j] != NULL; j++)
				free(F[i].list[j]);
			free(F[i].list);
		}
	}
	free(F);

	i = handle_form(path, file, args);

	for (j = 0; args[j] != NULL; j++)
		free(args[j]);
	free(args);

	return (i);
}

int
process_form(FORM *form, char *path)
{
	FILE *fp;
	char file[PATH_MAX], file2[PATH_MAX];
	struct stat sb;
	char *exec, *t, *p;
	size_t len;
	int fc, lcnt, i, j;
	FIELD **f;
	char **args, **nargs;
	CDKLABEL *label;
	char *msg[1];
	int key;

	/* handle the preform somewhere else */
	if (strcmp("pre", form_userptr(form)) == 0)
		return (process_preform(form, path));

	curs_set(0);
	*msg = catgets(catalog, 3, 17, "Are you sure? (Y/n)");
	label = newCDKLabel(cdkscreen, CENTER, CENTER, msg, 1, TRUE, FALSE);
	activateCDKLabel(label, NULL);
	key = waitCDKLabel(label, 0);
	destroyCDKLabel(label);
	touchwin(stdscr);
	wrefresh(stdscr);
	curs_set(1);
	if (key != 13 && key != 10 && key != 121 && key != 89) /* enter y Y */
		return (-1);

	if (lang_id == NULL) {
		snprintf(file, sizeof(file), "%s/%s", path, EXECFILE);
		snprintf(file2, sizeof(file2), "%s/%s", path, SCRIPTFILE);
	} else {
		snprintf(file, sizeof(file), "%s/%s.%s", path, EXECFILE,
		    lang_id);
		snprintf(file2, sizeof(file2), "%s/%s.%s", path, SCRIPTFILE,
		    lang_id);
		if (stat(file, &sb) != 0)
			snprintf(file, sizeof(file), "%s/%s", path, EXECFILE);
		if (stat(file2, &sb) != 0)
			snprintf(file2, sizeof(file2), "%s/%s", path,
			    SCRIPTFILE);
	}

	args = malloc(sizeof(char *) * 2);
	if (args == NULL)
		bailout("malloc: %s", strerror(errno));

	if ((fp = fopen(file, "r")) != NULL) {
		for (lcnt = 1; (exec = fgetln(fp, &len)) != NULL; ++lcnt) {
			if (len == 1)	/* skip blank */
				continue;
			if (exec[len - 1] == '#')	/* Skip comments */
				continue;
			if (exec[len - 1] != '\n') { /* corrupted? */
				warnx("%s: line %d corrupted", path, lcnt);
				continue;
			}
			exec[len - 1] = '\0';	/* NUL terminate */
			while (*exec != '\0' && isspace((unsigned char)*exec))
				++exec;
			if (*exec == '\0' || *exec == '#')
				continue;
			p = strsep(&exec, " ");
			for (i = 0; p != NULL; p = strsep(&exec, " "), i++) {
				nargs = realloc(args, sizeof(char *) * (i + 2));
				if (nargs == NULL)
					bailout("realloc: %s", strerror(errno));
				args = nargs;
				args[i] = strdup(p);
			}
			t = NULL;
		}
		fclose(fp);
	} else if (stat(file2, &sb) == 0) {
		t = strdup(file2);
		i = 1;
	} else
		bailout(catgets(catalog, 1, 13, "no files"));

	fc = lcnt = field_count(form);
	nargs = realloc(args, sizeof(char *) * (lcnt + 1 + i));
	if (nargs == NULL)
		bailout("realloc: %s", strerror(errno));
	args = nargs;

	f = form_fields(form);
	for (lcnt = 0; lcnt < fc; lcnt++) {
		if (F[lcnt].type != (PF_field)LABEL) {
			if (field_buffer(f[lcnt], 0) == NULL)
				args[i] = "";
			else
				args[i] = strdup(field_buffer(f[lcnt], 0));
			if (args[i] != NULL) {
				p = &args[i][strlen(args[i]) - 1];
				while (isspace((unsigned char)*p))
					*p-- = '\0';
			}
			i++;
		}
	}
	if (t != NULL)
		args[0] = t;
	args[i] = NULL;

	i = run_prog(1, args);

	for (j = 0; args[j] != NULL; j++)
		free(args[j]);
	free(args);

	return (i);
}

static FIELD **
make_fields(void)
{				/* create the fields */
	FIELD **f, **fields;
	int i;

	f = malloc(sizeof(FIELD *) * (MAX_FIELD + 1));
	if (f == NULL)
		bailout("malloc: %s", strerror(errno));
	fields = f;

	for (i = 0; i < MAX_FIELD && F[i].type; ++i, ++f)
		*f = (F[i].type)(&F[i]);

	*f = (FIELD *)0;

	return (fields);
}

struct cqForm cqFormHead, *cqFormHeadp;

static int
tstring(int max, char *string)
{
	char hold[10];
	int cur;

	if (max == 0)
		return (0);
	for (cur = 0; cur <= max; cur++) {
		snprintf(hold, sizeof(hold), "@@@%d@@@", cur);
		if (strcmp(hold, string) == 0)
			return (cur);
	}
	return (0);
}

static int
strlen_data(FTREE_ENTRY *ftp)
{
	int i, j;
	char *p, *q, *o;

	i = 0;
	switch (ftp->type) {
	case DATAT_BLANK:
		return (1);
		/* NOTREACHED */
		break;
	case DATAT_ENTRY:
	case DATAT_ESCRIPT:
	case DATAT_NESCRIPT:
	case DATAT_V4:
	case DATAT_V4SCRIPT:
	case DATAT_V6:
	case DATAT_V6SCRIPT:
	case DATAT_INVIS:
	case DATAT_NOEDIT:
		return (ftp->elen);
		/* NOTREACHED */
		break;
	case DATAT_LIST:
	case DATAT_FUNC:
	case DATAT_SCRIPT:
	case DATAT_MLIST:
	case DATAT_MFUNC:
	case DATAT_MSCRIPT:
		if (ftp->list == NULL)
			return (0);
		for (i = 0, j = 0; ftp->list[i] != NULL; i++) {
			size_t k;
			if ((k = strlen(ftp->list[i])) > j)
				j = k;
		}
		return (j);
		/* NOTREACHED */
		break;
	case DATAT_INTEGER:
	case DATAT_ISCRIPT:
		o = p = strdup(ftp->data);
		q = strsep(&p, ",");
		i = atoi(q);
		free(o);
		return (i);
		/* NOTREACHED */
		break;
	default:
		bailout(catgets(catalog, 1, 14, "invalid field type"));
		break;
	}
	return (1); /* eep! */
}

static void
gen_list(FTREE_ENTRY *ftp, int max, char **args)
{
	int i = 0;
	int lmax = 10;
	int cur;
	char *p, *q;
	char **nlist;

	ftp->list = malloc(sizeof(char *) * lmax);
	if (ftp->list == NULL)
		bailout("malloc: %s", strerror(errno));
	for (p = ftp->data; p != NULL;) {
		q = (char *)strsep(&p, ",");
		if (q != NULL) {
			cur = tstring(max, q);
			if (cur)
				ftp->list[i++] = strdup(args[cur-1]);
			else
				ftp->list[i++] = strdup(q);
		}
		if (i == lmax - 2) {
			nlist = realloc(ftp->list, sizeof(char*) * (lmax + 10));
			if (nlist == NULL)
				bailout("realloc: %s", strerror(errno));
			ftp->list = nlist;
			lmax += 10;
		}
	}
	ftp->list[i] = NULL;
}

static void
gen_func(FTREE_ENTRY *ftp, int max, char **args)
{
	int i, cur;
	char *p, *q;

	p = strsep(&ftp->data, ",");
	q = strsep(&ftp->data, ",");
	for (i = 0; func_map[i].funcname != NULL; i++)
		if (strcmp(func_map[i].funcname, p) == 0)
			break;

	if (func_map[i].function == NULL)
		bailout("%s: %s",
		    catgets(catalog, 1, 5, "function not found"), p);

	cur = tstring(max, q);
	if (cur)
		ftp->list = func_map[i].function(args[cur-1]);
	else
		ftp->list = func_map[i].function(q);
}

static void
gen_script(FTREE_ENTRY *ftp, char *dir, int max, char **args)
{
	char *p, *q, *qo, *po, *comm, *test, *n;
	FILE *file;
	char buf[PATH_MAX+30];
#if 0
	struct stat sb;
#endif
	size_t len;
	int i, cur;
	int lmax = 10;
	size_t l;
	char **nlist;

	qo = q = strdup(ftp->data);
	l = strlen(q) + 2;
	comm = malloc(sizeof(char) * l);
	if (comm == NULL)
		bailout("malloc: %s", strerror(errno));
	p = strsep(&q, ",");
	(void)strlcpy(comm, p, l);
	po = NULL;
	if (q != NULL) {
		for (po = p = strdup(q); p != NULL; p = strsep(&q, ",")) {
			(void)strlcat(comm, " ", l);
			for (test = p; *test != '\0'; test++)
				if (*test == ',') {
					*test = '\0';
					test++;
					q = test;
					break;
				}
			cur = tstring(max, p);
			if (cur) {
				l = strlen(comm) + strlen(args[cur-1]) + 2;
				n = realloc(comm, sizeof(char) * l);
				if (n == NULL)
					bailout("realloc: %s", strerror(errno));
				comm = n;
				(void)strlcat(comm, args[cur-1], l);
			} else {
				l = strlen(comm) + strlen(p) + 2;
				n = realloc(comm, sizeof(char) * l);
				if (n == NULL)
					bailout("realloc: %s", strerror(errno));
				comm = n;
				(void)strlcat(comm, p, l);
			}
		}
	}
	free(qo);
	if (po != NULL)
		free(po);

	snprintf(buf, sizeof(buf), "%s/%s", dir, comm);

#if 0
	if (stat(buf, &sb) != 0)
		bailout("%s: %s", buf, strerror(errno));
#endif

	file = popen(buf, "r");
	if (file == NULL)
		bailout("popen: %s", strerror(errno));

	ftp->list = malloc(sizeof(char *) * lmax);
	if (ftp->list == NULL)
		bailout("malloc: %s", strerror(errno));

	for (i = 0; (p = fgetln(file, &len)) != NULL;) {
		if (len <= 1)
			continue;
		ftp->list[i] = malloc(len);
		if (ftp->list[i] == NULL)
			bailout("malloc: %s", strerror(errno));
		memcpy(ftp->list[i], p, len);
		ftp->list[i][len - 1] = '\0';
		if (++i == lmax - 2) {
			nlist = realloc(ftp->list, sizeof(char *) * (lmax + 10));
			if (nlist == NULL)
				bailout("realloc: %s", strerror(errno));
			ftp->list = nlist;
			lmax += 10; 
		}
	}
	pclose(file);
	if (i == 0) {
		ftp->list[0] = "";
		ftp->list[1] = NULL;
	} else {
		ftp->list[i] = NULL;
	}
	free(comm);
}

static char *
gen_escript(FTREE_ENTRY *ftp, char *dir, int max, char **args)
{
	char *p, *q, *qo, *po, *test, *comm, *n;
	FILE *file;
	char buf[PATH_MAX+30];
	size_t len;
	int cur;
/*	struct stat sb; */
	size_t l;

	if (ftp->data == NULL)
		bailout(catgets(catalog, 1, 22,
		    "Null filename in escript argument"));
	else
		qo = q = strdup(ftp->data);

	l = strlen(q) + 2;
	comm = malloc(sizeof(char) * l);
	if (comm == NULL)
		bailout("malloc: %s", strerror(errno));
	p = strsep(&q, ",");
	(void)strlcpy(comm, p, l);
	po = NULL;
	if (q != NULL) {
		for (po = p = strdup(q); p != NULL; p = strsep(&q, ",")) {
			(void)strlcat(comm, " ", l);
			for (test = p; *test != '\0'; test++)
				if (*test == ',') {
					*test = '\0';
					test++;
					q = test;
					break;
				}
			cur = tstring(max, p);
			if (cur) {
				l = strlen(comm) + strlen(args[cur-1]) + 2;
				n = realloc(comm, sizeof(char) * l);
				if (n == NULL)
					bailout("realloc: %s", strerror(errno));
				comm = n;
				(void)strlcat(comm, args[cur-1], l);
			} else {
				l = strlen(comm) + strlen(p) + 2;
				n = realloc(comm, sizeof(char) * l);
				if (n == NULL)
					bailout("realloc: %s", strerror(errno));
				comm = n;
				(void)strlcat(comm, p, l);
			}
		}
	}

	free(qo);
	if (po != NULL)
		free(po);

	snprintf(buf, sizeof(buf), "%s/%s", dir, comm);

#if 0
	if (stat(buf, &sb) != 0)
		bailout("%s: %s", buf, strerror(errno));
#endif
	file = popen(buf, "r");
	if (file == NULL)
		bailout("popen: %s", strerror(errno));

	p = fgetln(file, &len);
	if (p != NULL) {
		p[len - 1] = '\0';	/* strip newline & NUL terminate */
		q = strdup(p);
	} else
		bailout("fgetln: on command '%s' gave error %s", buf,
			strerror(errno));

	pclose(file);
	free(comm);
	return (q);
}

static char *
gen_integer(FTREE_ENTRY *ftp, int max, char **args)
{
	char *q, *qo, *tmp;
	char buf[PATH_MAX+30];	
	int pre, min, maxi, cur;

	qo = q = strdup(ftp->data);
	tmp = strsep(&q, ",");
	cur = tstring(max, tmp);
	if (cur)
		pre = atoi(args[cur-1]);
	else
		pre = atoi(tmp);
	tmp = strsep(&q, ",");
	cur = tstring(max, tmp);
	if (cur)
		min = atoi(args[cur-1]);
	else
		min = atoi(tmp);
	tmp = strsep(&q, ",");
	cur = tstring(max, tmp);
	if (cur)
		maxi = atoi(args[cur-1]);
	else
		maxi = atoi(tmp);
	if (q == NULL)
		snprintf(buf, sizeof(buf), "%d,%d,%d", pre, min, maxi);
	else
		snprintf(buf, sizeof(buf), "%d,%d,%d,%s", pre, min, maxi, q);
	q = strdup(buf);
	free(qo);

	return (q);
}

static char *
gen_iscript(FTREE_ENTRY *ftp, char *dir, int max, char **args)
{
	char *p, *q, *qo, *po, *test, *comm, *tmp, *n;
	FILE *file;
	char buf[PATH_MAX+30];
/*	struct stat sb; */
	size_t len;
	int cur, min, maxi, pre;
	size_t l;

	qo = q = strdup(ftp->data);
	tmp = strsep(&q, ",");
	pre = atoi(tmp);
	tmp = strsep(&q, ",");
	min = atoi(tmp);
	tmp = strsep(&q, ",");
	maxi = atoi(tmp);

	l = strlen(q) + 2;
	comm = malloc(sizeof(char) * l);
	if (comm == NULL)
		bailout("malloc: %s", strerror(errno));
	tmp = strsep(&q, ",");
	(void)strlcpy(comm, tmp, l);
	po = NULL;
	if (q != NULL) {
		for (po = p = strdup(q); p != NULL; p = strsep(&q, ",")) {
			(void)strlcat(comm, " ", l);
			for (test = p; *test != '\0'; test++)
				if (*test == ',') {
					*test = '\0';
					test++;
					q = test;
					break;
				}
			cur = tstring(max, p);
			if (cur) {
				l = strlen(comm) + strlen(args[cur-1]) + 2;
				n = realloc(comm, sizeof(char) * l);
				if (n == NULL)
					bailout("realloc: %s", strerror(errno));
				comm = n;
				(void)strlcat(comm, args[cur-1], l);
			} else {
				l = strlen(comm) + strlen(p) + 2;
				n = realloc(comm, sizeof(char) * l);
				if (n == NULL)
					bailout("realloc: %s", strerror(errno));
				comm = n;
				(void)strlcat(comm, p, l);
			}
		}
	}
	free(qo);
	if (po != NULL)
		free(po);

	snprintf(buf, sizeof(buf), "%s/%s", dir, comm);

#if 0
	if (stat(buf, &sb) != 0)
		bailout("%s: %s", buf, strerror(errno));
#endif
	file = popen(buf, "r");
	if (file == NULL)
		bailout("popen: %s", strerror(errno));

	p = fgetln(file, &len);
	if (p != NULL) {
		p[len - 1] = '\0';	/* strip newline & NUL terminate */
		q = strdup(p);
	} else
		bailout("fgetln: on command '%s' gave error %s", buf,
			strerror(errno));

	pclose(file);
	snprintf(buf, sizeof(buf), "%d,%d,%d,%s", pre, min, maxi, q);
	free(q);
	q = strdup(buf);

	free(comm);

	return (q);
}

static void
form_generate(struct cqForm *cqf, char *basedir, char **args)
{
	int i = 0;
	int lrow = 0;
	int max, cur;
	char *p;
	FTREE_ENTRY *ftp;

	for (max = 0, cur = 0; args[max] != NULL; max++)
		continue;

	for (ftp = CIRCLEQ_FIRST(cqf); ftp != (void *)cqf;
	     ftp = CIRCLEQ_NEXT(ftp, cqFormEntries)) {

		F[i].type = LABEL;
		F[i].rows = 0;
		F[i].cols = 0;
		if (lrow >= ws.ws_row-6) {
			F[i].newpage = 1;
			lrow = 0;
		} else
			F[i].newpage = 0;
		F[i].frow = lrow;
		F[i].fcol = 0;
		F[i].v = strdup(ftp->desc);
		F[i].required = ftp->required;
		F[i].list = (char **)NULL;
		i++;

		if (ftp->type != DATAT_BLANK) {
			F[i].required = ftp->required;
			F[i].list = (char **)NULL;
			switch (ftp->type) {
			case DATAT_ENTRY:
				F[i].type = STRING;
				p = strsep(&ftp->data, ",");
				if (ftp->data == NULL)
					ftp->data = strdup("");
				ftp->elen = atoi(p);
				cur = tstring(max, ftp->data);
				if (cur)
					F[i].v = strdup(args[cur-1]);
				else
					F[i].v = strdup(ftp->data);
				break;
			case DATAT_LIST:
				F[i].type = ENUM;
				F[i].v = strdup(ftp->data);
				gen_list(ftp, max, args);
				F[i].list = ftp->list;
				break;
			case DATAT_FUNC:
				F[i].type = ENUM;
				F[i].v = strdup(ftp->data);
				gen_func(ftp, max, args);
				F[i].list = ftp->list;
				break;
			case DATAT_SCRIPT:
				F[i].type = ENUM;
				F[i].v = strdup(ftp->data);
				gen_script(ftp, basedir, max, args);
				F[i].list = ftp->list;
				break;
			case DATAT_MLIST:
				F[i].type = MULTI;
				F[i].v = strdup(ftp->data);
				gen_list(ftp, max, args);
				F[i].list = ftp->list;
				break;
			case DATAT_MFUNC:
				F[i].type = MULTI;
				F[i].v = strdup(ftp->data);
				gen_func(ftp, max, args);
				F[i].list = ftp->list;
				break;
			case DATAT_MSCRIPT:
				F[i].type = MULTI;
				F[i].v = strdup(ftp->data);
				gen_script(ftp, basedir, max, args);
				F[i].list = ftp->list;
				break;
			case DATAT_NOEDIT:
				F[i].type = NOEDIT;
				cur = tstring(max, ftp->data);
				if (cur)
					F[i].v = strdup(args[cur-1]);
				else
					F[i].v = strdup(ftp->data);
				ftp->elen = strlen(F[i].v);
				break;
			case DATAT_INVIS:
				F[i].type = INVIS;
				cur = tstring(max, ftp->data);
				if (cur)
					F[i].v = strdup(args[cur-1]);
				else
					F[i].v = strdup(ftp->data);
				ftp->elen = strlen(F[i].v);
				break;
			case DATAT_INTEGER:
				F[i].type = INTEGER;
				F[i].v = gen_integer(ftp, max, args);
				break;
			case DATAT_ISCRIPT:
				F[i].type = INTEGER;
				F[i].v = gen_iscript(ftp, basedir, max, args);
				break;					
			case DATAT_ESCRIPT:
				F[i].type = STRING;
				p = strsep(&ftp->data, ",");
				ftp->elen = atoi(p);
				F[i].v = gen_escript(ftp, basedir, max, args);
				if (strlen(F[i].v) > ftp->elen)
					ftp->elen = strlen(F[i].v);
				break;
			case DATAT_NESCRIPT:
				F[i].type = NOEDIT;
				F[i].v = gen_escript(ftp, basedir, max, args);
				ftp->elen = strlen(F[i].v);
				break;
			case DATAT_V4SCRIPT:
				F[i].type = IPV4;
				F[i].v = gen_escript(ftp, basedir, max, args);
				ftp->elen = 18;
				break;
			case DATAT_V4:
				F[i].type = IPV4;
				cur = tstring(max, ftp->data);
				if (cur)
					F[i].v = strdup(args[cur-1]);
				else {
					if (ftp->data == NULL)
						ftp->data = strdup("");
					F[i].v = strdup(ftp->data);
				}
				ftp->elen = 18;
				break;
			case DATAT_V6SCRIPT:
				F[i].type = IPV6;
				F[i].v = gen_escript(ftp, basedir, max, args);
				ftp->elen = 48;
				break;
			case DATAT_V6:
				F[i].type = IPV6;
				cur = tstring(max, ftp->data);
				if (cur)
					F[i].v = strdup(args[cur-1]);
				else {
					if (ftp->data == NULL)
						ftp->data = strdup("");
					F[i].v = strdup(ftp->data);
				}
				ftp->elen = 48;
				break;
			}
			F[i].rows = 1;
			F[i].rcols = F[i].cols = strlen_data(ftp);
			if (F[i].cols > 19) {
				F[i].bigfield = 1;
				F[i].cols = 19;
			} else
				F[i].bigfield = 0;
			F[i].newpage = 0;
			F[i].fcol = 55;
			F[i].frow = lrow;
			i++;
		}
		lrow++;
	}
	/* generate the final entry */
	F[i].type = (PF_field)NULL;
	F[i].rows = 0;
	F[i].cols = 0;
	F[i].frow = 0;
	F[i].fcol = 0;
	F[i].v = (char *)NULL;
	F[i].list = (char **)NULL;
	F[i].newpage = 0;
}

static void
tab_help(FORM *form)
{
	CDKLABEL *label;
	char *msg[4];
	char *buffer;
	FIELD *curfield;
	int lines;

	curfield = current_field(form);
	buffer = field_buffer(curfield, 1);
	if (buffer == NULL)
		return;
	if (*buffer == 's') {
		msg[0] = catgets(catalog, 3, 10, "The current field is a list "
		    "field, and selections can only be made from");
		msg[1] = catgets(catalog, 3, 11, "the pre-defined list.  "
		    "Please use TAB or the List(F4) command to edit.");
		msg[2] = catgets(catalog, 3, 12, "Press the ENTER key to "
		    "return from the list popup.");
		lines = 3;
	} else if (*buffer == 'm') {
		msg[0] = catgets(catalog, 3, 13, "The current field is a "
		    "multiple-selection list field.  The field can only");
		msg[1] = catgets(catalog, 3, 14, "be edited by issueing "
		    "the List(F4) command, and toggling desired options");
		msg[2] = catgets(catalog, 3, 15, "with the spacebar.  "
		    "Press the ENTER key to return from the list popup.");
		lines = 3;
	} else
		return;

	curs_set(0);
	label = newCDKLabel(cdkscreen, CENTER, CENTER, msg, lines, TRUE, FALSE);
	activateCDKLabel(label, NULL);
	waitCDKLabel(label, 0);
	destroyCDKLabel(label);
	touchwin(stdscr);
	wrefresh(stdscr);
	curs_set(1);
}

static void
invalid_field_help(void)
{
	CDKLABEL *label;
	char *msg[2];

	msg[0] = catgets(catalog, 3, 18, "The data entered in this field is "
	    "invalid.");
	msg[1] = catgets(catalog, 3, 19, "Please enter the correct "
	    "information before continuing.");

	curs_set(0);
	label = newCDKLabel(cdkscreen, CENTER, CENTER, msg, 2, TRUE, FALSE);
	activateCDKLabel(label, NULL);
	waitCDKLabel(label, 0);
	destroyCDKLabel(label);
	touchwin(stdscr);
	wrefresh(stdscr);
	curs_set(1);
}

int
handle_form(char *basedir, char *path, char **args)
{
	WINDOW *formwin, *boxwin;
	CDKLABEL *label;
	char *msg[1];
	FORM *menuform;
	FIELD **f;
	int done = FALSE;
	int c, i, j, fc;
	FTREE_ENTRY *ftp;

	CIRCLEQ_INIT(&cqFormHead);
	cqFormHeadp = &cqFormHead;
	if (scan_form(cqFormHeadp, path))
		return (1);

	F = calloc(sizeof(FIELD_RECORD), form_entries(&cqFormHead) * 2 + 1);
	if (F == NULL)
		bailout("calloc: %s", strerror(errno));
	fflush(NULL);

	curs_set(0);
	/* generate a label to let the user know we are thinking */
	msg[0] = catgets(catalog, 4, 2, "Generating form data, please wait");
	label = newCDKLabel(cdkscreen, CENTER, CENTER, msg, 1, TRUE, FALSE);
	activateCDKLabel(label, NULL);

	if (args == NULL)
		form_generate(&cqFormHead, basedir, (char **)NULL);
	else
		form_generate(&cqFormHead, basedir, args);

	if ((menuform = new_form(make_fields())) == NULL)
		bailout(catgets(catalog, 1, 15, "error return from new_form"));

	set_form_userptr(menuform, "post");

	destroyCDKLabel(label);

	wclear(stdscr);
	formwin = subwin(stdscr, ws.ws_row-5, ws.ws_col-2, 1, 1);
	boxwin = subwin(stdscr, ws.ws_row-3, ws.ws_col, 0, 0);
	bottom_help(2);
	box(boxwin, 0, 0);
	keypad(formwin, TRUE);
	set_form_sub(menuform, formwin);
	post_form(menuform);
	wrefresh(stdscr);
	wrefresh(boxwin);
	form_status(menuform);
	curs_set(1);
	while (!done) {
		pos_form_cursor(menuform);
		wrefresh(formwin);
		switch (form_driver(menuform, c = get_request(formwin))) {
		case E_OK:
			if (c == REQ_NEXT_PAGE || c == REQ_PREV_PAGE ||
			    c == REQ_FIRST_PAGE || c == REQ_LAST_PAGE ||
				c == REQ_NEXT_FIELD || c == REQ_PREV_FIELD)
				form_status(menuform);
			break;
		case E_UNKNOWN_COMMAND:
			done = my_driver(menuform, c, basedir);
			break;
		case E_REQUEST_DENIED:
			tab_help(menuform);
			break;
		case E_INVALID_FIELD:
			invalid_field_help();
			break;
		}
	}
	curs_set(0);
	fc = field_count(menuform);
	f = form_fields(menuform);
	unpost_form(menuform);
	for (; fc > 0; fc--)
		free_field(*f++);
	free_form(menuform);
	for (i = 0; F[i].type != NULL; i++) {
		free(F[i].v);
		if (F[i].list != NULL) {
			for (j = 0; F[i].list[j] != NULL; j++)
				free(F[i].list[j]);
			free(F[i].list);
		}
	}
	free(F);
	ftp = CIRCLEQ_FIRST(&cqFormHead);
	for (; !CIRCLEQ_EMPTY(&cqFormHead); ftp = CIRCLEQ_FIRST(&cqFormHead)) {
		CIRCLEQ_REMOVE(&cqFormHead, ftp, cqFormEntries);
		free(ftp->desc);
		free(ftp->origdata);
		free(ftp);
	}
	delwin(formwin);
	delwin(boxwin);
	wclear(stdscr);
	wrefresh(stdscr);
	if (done == 2)
		return (1);

	return (0);
}	

int
handle_preform(char *basedir, char *path)
{
	WINDOW *formwin, *boxwin;
	CDKLABEL *label;
	char *msg[1];
	FORM *menuform;
	FIELD **f;
	int done = FALSE;
	int c, i, j, fc;
	char *args[2];
	FTREE_ENTRY *ftp;

	CIRCLEQ_INIT(&cqFormHead);
	cqFormHeadp = &cqFormHead;
	if (scan_form(cqFormHeadp, path))
		return (1);

	F = calloc(sizeof(FIELD_RECORD), form_entries(&cqFormHead) * 2 + 1);
	if (F == NULL)
		bailout("calloc: %s", strerror(errno));
	fflush(NULL);

	curs_set(0);
	/* generate a label to let the user know we are thinking */
	msg[0] = catgets(catalog, 4, 2, "Generating form data, please wait");
	label = newCDKLabel(cdkscreen, CENTER, CENTER, msg, 1, TRUE, FALSE);
	activateCDKLabel(label, NULL);

	args[0] = NULL;
	form_generate(&cqFormHead, basedir, args);

	if ((menuform = new_form(make_fields())) == NULL)
		bailout(catgets(catalog, 1, 15, "error return from new_form"));

	set_form_userptr(menuform, "pre");

	destroyCDKLabel(label);

	wclear(stdscr);
	formwin = subwin(stdscr, ws.ws_row-5, ws.ws_col-2, 1, 1);
	boxwin = subwin(stdscr, ws.ws_row-3, ws.ws_col, 0, 0);
	bottom_help(2);
	box(boxwin, 0, 0);
	keypad(formwin, TRUE);
	set_form_sub(menuform, formwin);
	post_form(menuform);
	wrefresh(stdscr);
	form_status(menuform);
	curs_set(1);
	while (!done) {
		pos_form_cursor(menuform);
		wrefresh(formwin);
		switch (form_driver(menuform, c = get_request(formwin))) {
		case E_OK:
			if (c == REQ_NEXT_PAGE || c == REQ_PREV_PAGE ||
			    c == REQ_FIRST_PAGE || c == REQ_LAST_PAGE ||
				c == REQ_NEXT_FIELD || c == REQ_PREV_FIELD)
				form_status(menuform);
			break;
		case E_UNKNOWN_COMMAND:
			done = my_driver(menuform, c, basedir);
			break;
		case E_REQUEST_DENIED:
			tab_help(menuform);
			break;
		case E_INVALID_FIELD:
			invalid_field_help();
			break;
		}
	}
	curs_set(0);
	fc = field_count(menuform);
	f = form_fields(menuform);
	unpost_form(menuform);
	for (; fc > 0; fc--)
		free_field(*f++);
	free_form(menuform);
	if (done == 3) {
		for (i = 0; F[i].type != NULL; i++) {
			free(F[i].v);
			if (F[i].list != NULL) {
				for (j = 0; F[i].list[j] != NULL; j++)
					free(F[i].list[j]);
				free(F[i].list);
			}
		}
		free(F);
	}
	ftp = CIRCLEQ_FIRST(&cqFormHead);
	for (; !CIRCLEQ_EMPTY(&cqFormHead); ftp = CIRCLEQ_FIRST(&cqFormHead)) {
		CIRCLEQ_REMOVE(&cqFormHead, ftp, cqFormEntries);
		free(ftp->desc);
		free(ftp->origdata);
		free(ftp);
	}
	delwin(formwin);
	delwin(boxwin);
	wclear(stdscr);
	wrefresh(stdscr);
	if (done == 2)
		return (1);

	return (0);
}	
