/*	Copyright (c) 1988 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

/*
 *      Copyright (c) 1997, by Sun Microsystems, Inc.
 *      All rights reserved.
 */

#pragma ident	"@(#)newscreen.c	1.11	97/08/20 SMI"	/* SVr4.0 1.22	*/

/*LINTLIBRARY*/

#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<sys/types.h>
#include	"curses_inc.h"

#ifdef	_VR2_COMPAT_CODE
extern	char	_endwin;
#endif	/* _VR2_COMPAT_CODE */

/* 1200 is put at the 0th location since 0 is probably a mistake. */
static long    baud_convert[] = {
		    1200, 50, 75, 110, 135, 150, 200, 300, 600, 1200,
		    1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800,
		    115200, 153600, 230400, 307200, 460800
		};

static	char	isfilter = 0;
static	int	_chk_trm(void);
static	void	_forget(void);

/*
 * newscreen sets up a terminal and returns a pointer to the terminal
 * structure or NULL in case of an error.  The parameters are:
 *	type: terminal type
 *	lsize, csize, tabsize: physical sizes
 *	infptr, outfptr: input and output stdio stream file pointers
 */

SCREEN	*
newscreen(char *type, int lsize, int csize, int tabsize,
	FILE *outfptr, FILE *infptr)
{
	int		old_lines = LINES, old_cols = COLS, retcode;
#ifndef	_IOFBF
	char	*sobuf;
#endif	/* _IOBUF */
	WINDOW	*old_curscr = curscr;
	SCREEN	*old = SP;
	TERMINAL	*old_term = cur_term;

#ifdef	DEBUG
	if (outf == NULL) {
		outf = fopen("trace", "w");
		if (outf == NULL) {
			perror("trace");
			exit(-1);
		}
		setbuf(outf, (char *) NULL);
	}

	if (outf)
		fprintf(outf, "NEWTERM(type=%s, outfptr=%x %d, infptr=%x %d) "
		    "isatty(2) %d, getenv %s\n", type, outfptr,
		    fileno(outfptr), infptr, fileno(infptr), isatty(2),
		    getenv("TERM"));
#endif	/* DEBUG */


	/* read in terminfo file */

	if (setupterm(type, fileno(outfptr), &retcode) != 0)
		goto err2;

	/* the max length of a multi-byte character */
	_csmax = (cswidth[0] > cswidth[1]+1 ?
	    (cswidth[0] > cswidth[2]+1 ? cswidth[0] : cswidth[2]+1) :
	    (cswidth[1] > cswidth[2] ? cswidth[1]+1 : cswidth[2]+1));
	if (_csmax > CSMAX)
		goto err2;
	/* the max length of a multi-column character */
	_scrmax = _curs_scrwidth[0] > _curs_scrwidth[1] ?
	    (_curs_scrwidth[0] > _curs_scrwidth[2] ? _curs_scrwidth[0] :
	    _curs_scrwidth[2]) : (_curs_scrwidth[1] > _curs_scrwidth[2] ?
	    _curs_scrwidth[1] : _curs_scrwidth[2]);
	/* true multi-byte/multi-column case */
	_mbtrue = (_csmax > 1 || _scrmax > 1);

	if ((curs_errno = _chk_trm()) != -1) {
		(void) strcpy(curs_parm_err, cur_term->_termname);
		goto err2;
	}

	/* use calloc because almost everything needs to be zero */
	if ((SP = (SCREEN *) calloc(1, sizeof (SCREEN))) == NULL)
		goto err1;

	SP->term_file = outfptr;
	SP->input_file = infptr;

	/*
	* The default is echo, for upward compatibility, but we do
	* all echoing in curses to avoid problems with the tty driver
	* echoing things during critical sections.
	*/

	SP->fl_echoit = 1;

	/* set some fields for cur_term structure */

	(void) typeahead(fileno(infptr));
	(void) tinputfd(fileno(infptr));

	/*
	* We use LINES instead of the SP variable and a local variable because
	* slk_init and rip_init update the LINES value and application code
	* may look at the value of LINES in the function called by rip_init.
	*/

	/* LINTED */
	LINES = SP->lsize = lsize > 0 ? lsize : lines;

	/* force the output to be buffered */
#ifdef	_IOFBF
	(void) setvbuf(outfptr, (char *) NULL, _IOFBF, 0);
#else	/* _IOFBF */
	if ((sobuf = malloc(BUFSIZ)) == NULL) {
		curs_errno = CURS_BAD_MALLOC;
#ifdef	DEBUG
		strcpy(curs_parm_err, "newscreen");
#endif	/* DEBUG */
	}
	setbuf(outfptr, sobuf);
#endif	/* _IOFBF */

#ifdef	SYSV
	SP->baud = baud_convert[_BRS(PROGTTYS)];
#else	/* SYSV */
	SP->baud = baud_convert[_BR(PROGTTY)];
#endif	/* SYSV */

	/* figure out how much each terminal capability costs */
	_init_costs();

	/* initialize the array of alternate characters */
	(void) init_acs();

	SP->tcap = cur_term;

	/* set tty settings to something reasonable for us */
#ifdef	SYSV
	PROGTTYS.c_lflag &= ~ECHO;
	PROGTTYS.c_lflag |= ISIG;
	PROGTTYS.c_oflag &= ~(OCRNL|ONLCR); /* why would anyone set OCRNL? */
#else	/* SYSV */
	PROGTTY.sg_flags &= ~(RAW|ECHO|CRMOD);
#endif	/* SYSV */

	(void) cbreak();

	/* LINTED */
	COLS = SP->csize = csize > 0 ? csize : columns;
	if (tabsize == 0)
		tabsize = (init_tabs == -1) ? 8 : init_tabs;
	/* LINTED */
	SP->tsize = (short) tabsize;
#ifdef	DEBUG
	if (outf)
		fprintf(outf, "LINES = %d, COLS = %d\n", LINES, COLS);
#endif	/* DEBUG */

	if ((curscr = SP->cur_scr = newwin(LINES, COLS, 0, 0)) == NULL)
		goto err;

	SP->fl_endwin = 2;
#ifdef	_VR2_COMPAT_CODE
	_endwin = FALSE;
#endif	/* _VR2_COMPAT_CODE */
	curscr->_sync = TRUE;

	/*
	* This will tell _quick_echo(if it's ever called), whether
	* _quick_echo should let wrefresh handle everything.
	*/

	if (ceol_standout_glitch || (magic_cookie_glitch >= 0) ||
	    tilde_glitch || (transparent_underline && erase_overstrike)) {
		curscr->_flags |= _CANT_BE_IMMED;
	}
	if (!(SP->virt_scr = newwin(LINES, COLS, 0, 0)))
		goto err;
	_virtscr = SP->virt_scr;

	SP->virt_scr->_clear = FALSE;

	/* video mark map for cookie terminals */

	if (ceol_standout_glitch || (magic_cookie_glitch >= 0)) {
		int	i, nc;
		char	**marks;

		if ((marks = (char **) calloc((unsigned) LINES,
		    sizeof (char *))) == NULL)
			goto err;
		SP->_mks = marks;
		nc = (COLS / BITSPERBYTE) + (COLS % BITSPERBYTE ? 1 : 0);
		if ((*marks = (char *) calloc((unsigned) nc * LINES,
		    sizeof (char))) == NULL)
			goto err;
		for (i = LINES - 1; i-- > 0; ++marks)
			*(marks + 1) = *marks + nc;
	}

	/* hash tables for lines */
	if ((SP->cur_hash = (int *) calloc((unsigned) 2 * LINES,
	    sizeof (int))) == NULL)
		goto err;
	SP->virt_hash = SP->cur_hash + LINES;

	/* adjust the screen size if soft labels and/or ripoffline are used */
	if (_slk_init)
		(*_slk_init)();
	if (_rip_init)
		(*_rip_init)();

	if ((SP->std_scr = newwin(LINES, COLS, 0, 0)) == NULL) {
		/* free all the storage allocated above and return NULL */
err:
		delscreen(SP);
		COLS = old_cols;
		curscr = old_curscr;
		LINES = old_lines;
err1:
		SP = old;

		curs_errno = CURS_BAD_MALLOC;
#ifdef	DEBUG
		strcpy(curs_parm_err, "newscreen");
#endif	/* DEBUG */

err2:
		cur_term = old_term;
		return (NULL);
	}
#ifdef	DEBUG
	if (outf)
		fprintf(outf, "SP %x, stdscr %x, curscr %x\n",
		    SP, SP->std_scr, curscr);
#endif	/* DEBUG */

	if (((SP->imode = (enter_insert_mode && exit_insert_mode)) != 0) &&
	    ((SP->dmode = (enter_delete_mode && exit_delete_mode)) != 0)) {
		if (strcmp(enter_insert_mode, enter_delete_mode) == 0)
			SP->sid_equal = TRUE;
		if (strcmp(exit_insert_mode, exit_delete_mode) == 0)
			SP->eid_equal = TRUE;
	}
	SP->ichok = (SP->imode || insert_character || parm_ich);
	SP->dchok = (delete_character || parm_dch);

	stdscr = SP->std_scr;
	TABSIZE = SP->tsize;

	return (SP);
}

/*
 * check if terminal have capabilities to do basic cursor movements and
 * screen clearing
 */
static
_chk_trm(void)
{
	short	error_num = -1;
#ifdef	DEBUG
	if (outf)
		fprintf(outf, "chk_trm().\n");
#endif	/* DEBUG */

	if (generic_type)
		error_num = CURS_UNKNOWN;
	else {
		if (isfilter) {
			_forget();
			/* Only need to move left or right on current line */
			if (!(cursor_left || carriage_return ||
			    column_address || parm_left_cursor)) {
				goto out_stupid;
			}
		} else {
			if ((hard_copy || over_strike) ||
			/* some way to move up, down, left */
			    (!(cursor_address) &&
			    (!((cursor_up || cursor_home) && cursor_down &&
			    (cursor_left || carriage_return)))) ||
			    (!clear_screen)) {
out_stupid:
				error_num = CURS_STUPID;
			}
		}
	}
	return (error_num);
}

int
filter(void)
{
	isfilter = 1;
	return (OK);
}

/*
 * if (for some reason) user assumes that terminal has only one line,
 * disable all capabilities that deal with non-horizontal cursor movement
 */
static void
_forget(void)
{
	row_address = cursor_address = clear_screen = parm_down_cursor =
	    cursor_up = cursor_down = NULL;
	cursor_home = carriage_return;
	lines = 1;
}
