#ifndef lint
static char Sccsid[] = "@(#)screen.c	3.3    DeltaDate 12/9/90    ExtrDate 12/9/90";
#endif

/*	SCREEN.C	*/
/*	This subroutine is used to format a screen and handle
**	input to it. It expects as input:
**		1. A pointer to the title of the screen
**		2. A pointer to an array of structures describing
**		   each field.
**		3. An (optional) pointer to a footer line.
**		4. An (optional) pointer to an integer to contain
**		   the field number in which the pointer was left.
**		5. A flag which if TRUE makes the screen display only.
**	It returns the number of fields modified.
**	The external character lastchar will return the last charater input.
**
**	The field definition structures contain:
**		1. A character pointer to the title of the field
**		2. The maximum length of the field
**		3. A pointer to the variable to hold the result
**		4. An (optional) pointer to the default value
**		5. An (optional) row for the title
**		6. An (optional) column for the title
**		7. An (optional) row for the data
**		8. An (optional) column for the data
**		9. An (optional) format for the data
**
**	It allows the user to enter:
**		1. CR   End of Input
**		2. ->   Move right
**		3. <-   Move left
**		4. TAB  Move to next field, wrap around to top
**		5. BACK-TAB Move to previous field, wrap to the bottom field
**		6. LINE-ERASE Erase to end of field
**		7. CHAR-INSERT Insert a blank and move everything right
**		8. CHAR-DELETE Delete a character and move everything left
**		9. DOWN-ARROW Same as TAB
**	       10. UP-ARROW Same as BACK-TAB
**	       11. F2	Same as CHAR-INSERT
**	       12. F3	Same as CHAR-DELETE
**	       13. F4	Same as LINE-ERASE
**	       14. F5	Go to the first field on the next page of a
**			multi-page form
**	       15. F6	Go to the previous page
*/

#include "cardfile.h"
#include "stdio.h"
#include "ascii.h"
#include <ctype.h>
#include <signal.h>
#ifdef TERMCAP
# ifdef TERMIO
#include <termio.h>
# endif
#define tparm(a, line, col)	tgoto(a, col, line)
#define	putp(a)		tputs(a, 12, mputc)
#else
#include <curses.h>
#include <term.h>
#endif
#ifdef BSD_TTY
#include <sgtty.h>
#endif
#ifdef BSD_SIG
#include <setjmp.h>
jmp_buf	jbuf;
#endif

#ifdef TERMCAP
extern char *tgoto(), *getenv();
extern char
	*clear_screen,
	*clr_eol,
	*enter_dim_mode,
	*enter_blink_mode,
	*exit_attribute_mode,
	*keypad_xmit,
	*keypad_local,
	*cursor_address,
	*cursor_left,
	*cursor_right;
int	mputc();
#endif


extern struct cchar	*cc_head;
extern	char	func_label[];

char		lastchar;

static  int	flen;
static  char	*cp;
static  int	changes;
static	int	fmt_msg = FALSE;
static  struct	SFdata {
		int	f_page;
		int	f_tline;
		int	f_tcol;
		char	*f_title;
		int	f_dline;
		int	f_dcol;
		char	*f_dfmt;
		int	f_length;
		char	*f_result;
		char	*f_dfault;
	} scr_fields[MAXFLDS+1];
static	struct	SFdata	*fp, *first_field;
static	int	page_num;
static	int	read_only;	/* true if not to be modified */

SIGRTN	ctl_timeout();
static	int	timedout;

int
screen(first, vardata, last, left, ro)
char    *first, *last;
struct  Sdata   *vardata;
int	*left;
int	ro;
{
    int		inp;
    char	t_last[81];
    char	mbuf[81];
    register	struct	cchar *sp;
    struct	SFdata	*last_field;

    read_only = ro;
#if DEBUG > 0
    fprintf(stderr,"screen: called first=%s\n",first);
#endif
    /*
     * Build the scr_fields table
     */
    scr_fields[0].f_page = 1;
    scr_fields[0].f_tline = 2;
    for (fp = scr_fields ; vardata->S_title != NULL ; ++vardata , ++fp) {
	if (vardata->S_Lrow != -1) {
	    fp->f_tline = vardata->S_Lrow;
	}
	if (vardata->S_Lcol != -1) {
	    fp->f_tcol = vardata->S_Lcol;
	} else {
	    fp->f_tcol = 4;
	}
	if (vardata->S_Drow != -1) {
	    fp->f_dline = vardata->S_Drow;
	} else {
	    fp->f_dline = fp->f_tline;
	}
	if (vardata->S_Dcol != -1) {
	    fp->f_dcol = vardata->S_Dcol;
	} else {
	    if (vardata->S_length > 1) {
		fp->f_dcol = fp->f_tcol + strlen(vardata->S_title) + 6;
		if (vardata->S_length > 99)
		    fp->f_dcol += 2;
		else if (vardata->S_length > 9)
		    fp->f_dcol += 1;
	    } else {
		fp->f_dcol = fp->f_tcol + strlen(vardata->S_title) + 3;
	    }
	}
	fp->f_title = vardata->S_title;
	fp->f_length = vardata->S_length;
	fp->f_result = vardata->S_result;
	fp->f_dfault = vardata->S_dfault;
	fp->f_dfmt = vardata->S_Dfmt;
	/* check if too much for this screen */
	if ( (fp->f_tline + (fp->f_dcol + fp->f_length + SWIDTH-1)/SWIDTH)
	   > SLENGTH - 4) {
	    (fp+1)->f_page = fp->f_page + 1;
	    (fp+1)->f_tline = 2;
	} else {
	    (fp+1)->f_page = fp->f_page;
	    (fp+1)->f_tline = fp->f_tline + 1
		+ (fp->f_dcol + fp->f_length + SWIDTH-1)/SWIDTH;
	}
    }
    fp->f_page = fp->f_tline = 0;
#if DEBUG > 0
    fprintf(stderr, "%-4s  %-4s  %-10.10s %-5s  %-4s\n",
	"", "", "", "data", "data");
    fprintf(stderr, "%-4s  %-4s  %-10.10s %-5s  %-4s\n",
	"page", "line", "title", "start", "size");
    for (fp = scr_fields ; fp->f_page != 0 ; ++fp) {
	fprintf(stderr, "%4d  %4d  %-10.10s %5d %4d\n",
	    fp->f_page, fp->f_tline, fp->f_title, fp->f_dcol, fp->f_length);
    }
#endif
    /*
     *	Format the screen
     */
    changes = 0;
    noecho();
    /* Loop for all pages */
    for (page_num = 1 , first_field = scr_fields
	 ; first_field->f_page != 0 ; ) {
#if DEBUG > 1
    fprintf(stderr,"screen: outer loop, page_num=%d\n",page_num);
#endif
	putp(clear_screen);			/* clear screen */
	putp(tparm(cursor_address, 0, 9));
	fputs(first, stdout);			/* display title */
	/* Loop for each field on 1 page */
	for (fp = first_field ; fp->f_page == page_num ; ++fp) {
#if DEBUG > 1
	    fprintf(stderr,"screen: inner loop, page_num=%d, line=%d\n",
		page_num, fp->f_tline);
#endif
	    putp(tparm(cursor_address, fp->f_tline, fp->f_tcol-1));
	    putp(enter_dim_mode);
	    if (fp->f_length > 1) {
		putp(tparm(cursor_address, fp->f_tline, fp->f_tcol));
		printf("%s(%d):", fp->f_title, fp->f_length);
	    } else {
		putp(tparm(cursor_address, fp->f_tline, fp->f_tcol));
		printf("%s:", fp->f_title);
	    }
	    putp(exit_attribute_mode);
	    if (fp->f_dfault != 0) {
		strcpy(fp->f_result, fp->f_dfault);
		putp(tparm(cursor_address, fp->f_dline, fp->f_dcol));
		fputs(fp->f_dfault, stdout);
		flen = strlen(fp->f_dfault);
	    } else {
		flen = 0;
		fp->f_result[0] = '\0';
	    }
	    putchar('\n');
	    for (flen=strlen(fp->f_result); flen <= fp->f_length; flen++)
		fp->f_result[flen] = '\0';
	}
	/* check if too much for this screen */
	if (fp->f_page != 0) {
	    sprintf(t_last, "%-64s[CONT]", last?last:"");
	} else {
	    strcpy(t_last, last?last:"");
	}
	/* Write instructions if any */
	if (*t_last != '\0') {
	    putp(tparm(cursor_address, MSGLINE, 9));
	    fputs(t_last, stdout);
	}
	putp(tparm(cursor_address, SLENGTH-1, 0));
	fputs(func_label, stdout);
	fp = first_field;
	cp = fp->f_result;
	resetcursor();
	flen = fp->f_length;
	
	/*
	 *	Process the user input
	 */
	while ((inp = rawgetchar())) {
	    lastchar = inp;
	    if (inp == '\n' || inp == '\r') {
		/* check format first */
		if (! read_only && fp->f_dfmt[0] && fp->f_result[0]) {
		    if (fmt_chk(fp->f_result, fp->f_dfmt) != 0) {
			fmt_msg = TRUE;
			sprintf(mbuf, "Invalid format, should be %.40s", fp->f_dfmt);
			msg(mbuf);
			rawputchar(BEL);
			cp = fp->f_result;
			flen = fp->f_length;
			resetcursor();
			continue;
		    } else {
			if (fmt_msg) {
			    msg("");
			    fmt_msg = FALSE;
			}
		    }

		}
		if (fmt_msg) {
		    msg("");
		    fmt_msg = FALSE;
		}
		break;
	    }
	    /*
	     * Check for control key sequence
	     */
	    timedout = FALSE;
	    signal(SIGALRM, ctl_timeout);
	    alarm(1);
	    sp = cc_head;
	    while (sp) {
		if (inp == sp->ch) {
		    inp = process_ctl(sp);
		    break;
		}
		sp = sp->alt;
	    }
	    alarm(0);
	    signal(SIGALRM, SIG_DFL);
	    if (sp != NULL) {		/* control character match found */
		if (inp == -1)	/* page switch */
		    break;
		if (read_only && timedout)
		    return(0);
		continue;
	    }
	    if (read_only) {
		return(0);
	    }
	    /*
	     * Process a normal character
	     */
	    /* Control characters are illegal (also catches illegal controls) */
	    if (! isascii(inp) || iscntrl(inp)) {
		rawputchar(BEL);
		continue;
	    }
	    rawputchar(inp);		/* echo character */
	    *cp++ = inp;
	    ++changes;
	    if (--flen <= 0)		/* reached end of field */
		nextfield(scr_fields);
	}   /* End of INPut Loop */
#if DEBUG > 0
	fprintf(stderr,"screen: end of outer loop, inp=%d\n",inp);
#endif
	last_field = fp;
	if (inp == '\n' || inp == '\r')
	    break;
    }

    echo();
    if (left != NULL)
	*left = last_field - scr_fields;
#ifdef DEBUG
    fprintf(stderr,"screen exit, left=%d, changes=%d\n",
	    (left ? *left : -1), changes);
#endif
    return changes;
}


int
process_ctl(ccp)
struct cchar *ccp;
{
    int		ch;

    /* check for end of control sequence */
    if (ccp->action) {
	return((*ccp->action)(scr_fields));
    }
#ifdef BSD_SIG
    if (setjmp(jbuf) != 0) {
	/* return from interupt */
	return(0);
    }
#endif
    if ((ch = rawgetchar()) == EOF) {
	if (! read_only)
	    rawputchar(BEL);
	return(0);
    }
    lastchar = ch;
    ccp = ccp->next;
    while (ccp) {
	if (ch == ccp->ch)
	    return(process_ctl(ccp));
	ccp = ccp->alt;
    }
    rawputchar(BEL);
    return(0);		/* illegal sequence */
}

SIGRTN
ctl_timeout()
{
    timedout = TRUE;
#ifdef BSD_SIG
    longjmp(jbuf, 1);
#endif
}

/*
 *	These are the action routines to manage the fields on the screen.
 * Each routine is called from the getchar loop above when an action key
 * is read. The routine is passed the screen data structure.
 *
 * Globally they use
 * fp		pointer to the current entry in the fields table (SFtable)
 * cp		character position, i.e., character in which next input
 *		is to be placed
 * flen		length of input field
 * changes	flag if screen has been modified
 * page_num	the page of fields displayed
 * first_field	the first field displayed on the screen
 */

/*ARGSUSED*/
nextpage(field_tbl)
struct	SFdata	field_tbl[];
{
    char	mbuf[81];

    /* see if format needs to be checked */
    if (fp->f_dfmt[0] && fp->f_result[0]) {
	if (fmt_chk(fp->f_result, fp->f_dfmt) != 0) {
	    fmt_msg = TRUE;
	    sprintf(mbuf, "Invalid format, should be %.40s", fp->f_dfmt);
	    msg(mbuf);
	    rawputchar(BEL);
	    cp = fp->f_result;
	    flen = fp->f_length;
	    resetcursor();
	    return(0);
	} else {
	    if (fmt_msg) {
		msg("");
		fmt_msg = FALSE;
	    }
	}
    }
    if (fmt_msg) {
	msg("");
	fmt_msg = FALSE;
    }

    page_num++;
    for (first_field = field_tbl ;
	 first_field->f_page != page_num && first_field->f_page != 0 ;
	 ++first_field)
	;
    if (first_field->f_page == 0) {	/* wrap around */
	page_num = 1;
	first_field = field_tbl;
    }
#if DEBUG > 0
    fprintf(stderr, "nextpage: page=%d, first_field=%d\n",
	page_num, first_field-field_tbl);
#endif
    return(-1);		/* force termination of input loop */
}


prevpage(field_tbl)
struct	SFdata	field_tbl[];
{
    register struct	SFdata	*t_fp;
    char	mbuf[81];

    /* see if format needs to be checked */
    if (fp->f_dfmt[0] && fp->f_result[0]) {
	if (fmt_chk(fp->f_result, fp->f_dfmt) != 0) {
	    fmt_msg = TRUE;
	    sprintf(mbuf, "Invalid format, should be %.40s", fp->f_dfmt);
	    msg(mbuf);
	    rawputchar(BEL);
	    cp = fp->f_result;
	    flen = fp->f_length;
	    resetcursor();
	    return(0);
	} else {
	    if (fmt_msg) {
		msg("");
		fmt_msg = FALSE;
	    }
	}
    }
    if (fmt_msg) {
	msg("");
	fmt_msg = FALSE;
    }

    if (--page_num < 1) {	/* wrap around */
	for (t_fp = field_tbl ; (t_fp+1)->f_page != 0 ; ++t_fp)
	    ;
	page_num = t_fp->f_page;
    }
    for (first_field = field_tbl ;
	 first_field->f_page != page_num && first_field->f_page != 0 ;
	 ++first_field)
	;
#if DEBUG > 0
    fprintf(stderr, "prevpage: page=%d, first_field=%d\n",
	page_num, first_field-field_tbl);
#endif
    return(-1);		/* force termination of input loop */
}


/*ARGSUSED*/
nextfield(dummy)
struct	SFdata	dummy[];
{
    register int page;
    char	mbuf[81];

    /* see if format needs to be checked */
    if (fp->f_dfmt[0] && fp->f_result[0]) {
	if (fmt_chk(fp->f_result, fp->f_dfmt) != 0) {
	    fmt_msg = TRUE;
	    sprintf(mbuf, "Invalid format, should be %.40s", fp->f_dfmt);
	    msg(mbuf);
	    rawputchar(BEL);
	    cp = fp->f_result;
	    flen = fp->f_length;
	    resetcursor();
	    return(0);
	} else {
	    if (fmt_msg) {
		msg("");
		fmt_msg = FALSE;
	    }
	}
    }
    if (fmt_msg) {
	msg("");
	fmt_msg = FALSE;
    }

    page  = fp->f_page;
    ++fp;
    if (fp->f_page != page) {		/* wrap-around */
	fp = first_field;
    }
    cp = fp->f_result;
    flen = fp->f_length;
    resetcursor();
    return(0);
}


/*ARGSUSED*/
prevfield(dummy)
struct	SFdata	dummy[];
{
    register int page;
    char	mbuf[81];

    /* see if format needs to be checked */
    if (fp->f_dfmt[0] && fp->f_result[0]) {
	if (fmt_chk(fp->f_result, fp->f_dfmt) != 0) {
	    fmt_msg = TRUE;
	    sprintf(mbuf, "Invalid format, should be %.40s", fp->f_dfmt);
	    msg(mbuf);
	    rawputchar(BEL);
	    cp = fp->f_result;
	    flen = fp->f_length;
	    resetcursor();
	    return(0);
	} else {
	    if (fmt_msg) {
		msg("");
		fmt_msg = FALSE;
	    }
	}
    }
    if (fmt_msg) {
	msg("");
	fmt_msg = FALSE;
    }

    if (cp == fp->f_result) {   /* at beginning of field */
	if (fp == first_field) {	/* wrap around */
	    page = fp->f_page;
	    while ((++fp)->f_page == page)
		;
	}
	--fp;
    }
    cp = fp->f_result;
    flen = fp->f_length;
    resetcursor();
    return(0);
}


goright(field_tbl)
struct	SFdata	field_tbl[];
{
    register int	pos;

    if (--flen <= 0)
	nextfield(field_tbl);
    if (*cp == '\0')
	*cp = ' ';
    cp++;
    pos = cp - fp->f_result + fp->f_dcol;
    if (pos % SWIDTH == 0) {
	resetcursor();
    } else {
	putp(cursor_right);
    }
    return(0);
}


goleft(field_tbl)
struct	SFdata	field_tbl[];
{
    register int	pos;

    if (++flen > fp->f_length) {
	prevfield(field_tbl);
    } else {
	--cp;
	pos = cp - fp->f_result + fp->f_dcol;
	if (pos % SWIDTH == SWIDTH - 1) {
	    resetcursor();
	} else {
	    putp(cursor_left);
	}
    }
    return(0);
}


/*ARGSUSED*/
ferase(dummy)
struct  Sdata   *dummy;
{
    register char	*cpt;
    register int	flent;
    
    cpt = cp;
    flent = flen;
    while (flent-- > 0) {
	*(cpt++) = '\0';
	rawputchar(' ');
    }
    resetcursor();
    ++changes;
    return(0);
}


/*ARGSUSED*/
delchar(dummy)
struct  Sdata   *dummy;
{
    strcpy(cp, cp+1);
    fputs(cp, stdout);
    rawputchar(' ');
    resetcursor();
    ++changes;
    return(0);
}


/*ARGSUSED*/
inschar(dummy)
struct  Sdata   *dummy;
{
    mvright(cp+1, cp);
    *cp = ' ';
    fputs(cp, stdout);
    resetcursor();
    ++changes;
    return(0);
}


mvright(d, s)
char    *d, *s;
{
    register int	slen;
    
    slen = strlen(s);
    while (slen-- >= 0)
	*(d+slen) = *(s+slen);
    return(0);
}


resetcursor()
{
    register int	flent;

    flent = cp - fp->f_result + fp->f_dcol;
    putp(tparm(cursor_address, fp->f_dline + flent/SWIDTH, flent%SWIDTH));
}

#ifdef BSD_TTY
static struct	sgttyb	instty, outstty;
#else	/* SYSV_TTY */
static struct	termio	instty, outstty;
#endif


void
setupscr()
{
    
    setbuf(stdout, NULL);
    setbuf(stdin, NULL);
#ifdef BSD_TTY
    ioctl(0, TIOCGETP, &outstty);
    instty.sg_ispeed = outstty.sg_ispeed;
    instty.sg_ospeed = outstty.sg_ospeed;
    instty.sg_erase = outstty.sg_erase;
    instty.sg_kill = outstty.sg_kill;
    instty.sg_flags = RAW;
    ioctl(0, TIOCSETP, &instty);
#else	/* SYSV_TTY */
    ioctl(0, TCGETA, &outstty);
    instty.c_iflag = outstty.c_iflag;
    instty.c_oflag = outstty.c_oflag;
    instty.c_cflag = outstty.c_cflag;
    instty.c_lflag = 0;		/* turn off icanon, echo */
    instty.c_line = outstty.c_line;
    instty.c_cc[VINTR] = outstty.c_cc[VINTR];
    instty.c_cc[VQUIT] = outstty.c_cc[VQUIT];
    instty.c_cc[VERASE] = outstty.c_cc[VERASE];
    instty.c_cc[VKILL] = outstty.c_cc[VKILL];
    instty.c_cc[VMIN] = 1;
    instty.c_cc[VTIME] = 0;
    instty.c_cc[6] = outstty.c_cc[6];
    instty.c_cc[7] = outstty.c_cc[7];
    ioctl(0, TCSETAW, &instty);
#endif
#ifdef TERMINFO
    setupterm((char*)0, 1, (int*)0);
#else
    setterm(getenv("TERM"));
#endif
    setupkeys();
    putp(keypad_xmit);
}

SIGRTN
getout()
{

    putp(keypad_local);
    putp(clear_screen);
#ifdef BSD_TTY
    ioctl(0, TIOCSETP, &outstty);
#else	/* SYSV_TTY */
    ioctl(0, TCSETAW, &outstty);
#endif
    exit(0);
}

