/*	Copyright (C) 1990 Free Software Foundation, Inc.

This file is part of Oleo, the GNU Spreadsheet.

Oleo is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.

Oleo is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Oleo; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include "funcdef.h"

#include <fcntl.h>
#include <errno.h>
#include <ctype.h>

#ifdef __TURBOC__
#include <conio.h>
#include <stdarg.h>
#include <stdio.h>

#define COLS 80
#define LINES 24

#define endwin()	clrscr()
#define initscr()
#define scrollok(x,y)
#define cbreak()
#define noecho()
#define nonl()
#define nl()
#define refresh()
#define mvaddch(r,c,ch)	move(r,c),addch(ch)
#define mvaddstr(r,c,st) move(r,c),addstr(st)
#define clear()		clrscr()
#define clrtoeol()	clreol()
#define addstr(str)	cputs(str)
#define addch(ch)	putch(ch)
#define move(y,x)	gotoxy(x+1,y+1)
#define getyx(nul,yy,xx) yy=wherey()-1,xx=wherex()-1
#define delch()		movetext(col+2,1,COLS,1,col+1,1),puttext(80,1,80,1,blank_text)
#define insch(ch)	movetext(col,1,COLS-1,1,col+1,1),putch(ch)
#define standout()	textattr(0x7<<4)
#define standend()	textattr(0x7)
#define beep()		putchar('\007')
#define stdscr	0
static void hack_standout EXT4(int, int, int, int);
static char blank_text[] = { ' ', 0x07 };
static void printw();

#else
#undef USG
#undef NULL
#include <curses.h>

extern void tputs();

extern int wclear();
extern int wrefresh();
extern int endwin();
extern int wmove();
extern int wclrtoeol();
extern int printw();
extern int wdelch();
extern int waddch();
extern int winsch();
extern int waddstr();
#ifndef amiga
extern int wstandout();
extern int wstandend();
#endif
static void beep();

#endif
#include "sysdef.h"

#include "global.h"
#include "cell.h"
#include "kbd.h"

/* This must agree with io_term.c */
struct line {
	int alloc;
	char *buf;
};

/* Window flags:
	0x01	Locked horizontally
	0x02	Locked vertically
	0x04	Page Horizontally
	0x08	Page Vertically
	0x10	Edges disabled
	0x20	Edges standout
 */
#define WIN_LCK_HZ	0x01
#define WIN_LCK_VT	0x02
#define WIN_PAG_HZ	0x04
#define WIN_PAG_VT	0x08
#define WIN_EDGES	0x10
#define WIN_EDGE_REV	0x20

#define MIN_WIN_HEIGHT	(cwin->win_flags&WIN_EDGES ? 2 : 1)
#define MIN_WIN_WIDTH	(cwin->win_flags&WIN_EDGES ? 6 : 1)

struct window {
	struct rng screen;		/* Cells that are onscreen in this
					   window */
	VOIDSTAR *win_slops;		/* Slops in this window */
	int win_over;		/* Where the data in this window starts */
	int win_down;		/* on the screen.  */

	int win_flags;			/* Various flags */

		/* -1 if this window isn't linked to any others, else
		   contains the index into wins of the window this one is
 		   linked to */
	int win_linked;

		/* Number of lines of spreadsheet that can fit in this window.
		   This only changes when the screen is resized,
		   win->flags&WIN_EDGES changes, or a window is either
 		   created or destroyed */
	int numr;

		/* Number of text columns that can fit in this window.
		   This changes when the screen is resized,
		   win->flags&WIN_EDGES changes, a window is created or
		   destoryed, or win->lh_wid changes.  In the last case
		   win->numc+win->lh_wid remains a constant. */
	int numc;

		/* Number of characters taken up by the row numbers at the
		   left hand edge of the screen.  Zero if edges is
		   win->flags&WIN_EDGES is off (by definition).  Seven (or
		   five) if win->flags&WIN_PAG_HZ (to make things easier).
		   Ranges between three "R9 " to seven "R32767 " depending on
		   the number of the highest row on the screen.  */
	int lh_wid;

		/* Cursor row/column in this window */
		/* Note that the external variables curow, cucol are used for
		   the currently active cursor position, so if you want
		   cwin->curow and cwin->cucol to be accurate, you have to
		   set them yourself. */
	CELLREF curow,cucol;
};

#define LINE_MIN 28

/* in io_utils.c */
extern void flush_slops EXT1(VOIDSTAR);
extern int find_slop EXT5(VOIDSTAR, CELLREF, CELLREF, CELLREF *, CELLREF *);
extern void kill_slop EXT4(VOIDSTAR, CELLREF, CELLREF, CELLREF);
extern void change_slop EXT6(VOIDSTAR, CELLREF, CELLREF, CELLREF, CELLREF, CELLREF);
extern void set_slop EXT4(VOIDSTAR *, CELLREF, CELLREF, CELLREF);

extern char *print_cell EXT1(CELL *);
extern char *adjust_prc EXT5(char *, CELL *, int, int, int);

extern char *cell_value_string EXT2(CELLREF, CELLREF);

/* in io_term.c */
extern void set_line EXT2(struct line *,char *);
extern int get_chr EXT0();
extern int global_cmd EXT1(int);

extern struct keymap main_map;
extern struct keymap meta_map[];
extern int topclear;
extern CELLREF mkrow,mkcol;
extern CELLREF setrow,setcol;
extern unsigned int how_many;
extern const int rowmagic[],colmagic[];
#ifdef TEST
extern int dbg_do_stderr;
#endif

/* in regions.c */
extern void set_rng EXT5(struct rng *,CELLREF, CELLREF, CELLREF, CELLREF);

void cur_status EXT0();
void pr_cell EXT3(CELLREF, CELLREF, CELL *);
void set_numcols EXT2(struct window *, CELLREF);
void disp_scrn EXT0();

static void find_nonzero_width EXT1(struct window *);
static void hide_cell_cursor EXT0();
static void display_cell_cursor EXT0();

static void recenter_window EXT1(struct window *);
static void pr_cell_win EXT4(struct window *, CELLREF, CELLREF, CELL *);

/* Constants */
const char numb_oflo[] ="\
##############################################################################\
##############################################################################\
##############################################################################\
######";


static int nwin;
static struct window *wins;
static struct window *cwin;

int status = 2;
int input = 1;

int redrew = 0;
int textout = 0;

#define TOPLN	0x2
#undef CTRL
#define CTRL(X) ((X)&037)
#define META(X) ((X)|0200)

#define L_TOGGLE_OVER	 0
#define L_BEG_LINE	 1
#define L_END_LINE	 2
#define L_BK_CHR	 3
#define L_BK_WORD	 4
#define L_BK_DEL_CHR	 5
#define L_BK_DEL_WORD	 6
#define L_BK_DEL_END	 7
#define L_FW_CHR	 8
#define L_FW_WORD	 9
#define L_FW_DEL_CHR	10
#define L_FW_DEL_WORD	11
#define L_FW_DEL_END	12
#define L_FINISH	13
#define L_INS_EXPR	14
#define L_INS_VAL	15
#define L_INS_REL	16
#define L_INS_ABS	17
#define L_INS_CHR	18

struct cmd_func edit_funcs[] = {
{	"toggle-over-write",	0,	TOPLN,	0 },
{	"cursor-begin-line",	0,	TOPLN,	0 },
{	"cursor-end-line",	0,	TOPLN,	0 },
{	"cursor-back-char",	"n",	TOPLN,	0 },
{	"cursor-back-word",	"n",	TOPLN,	0 },
{	"delete-prev-char",	"n",	TOPLN,	0 },
{	"delete-prev-word",	"n",	TOPLN,	0 },
{	"delete-to-start",	0,	TOPLN,	0 },
{	"cursor-fwd-char",	"n",	TOPLN,	0 },
{	"cursor-fwd-word",	"n",	TOPLN,	0 },
{	"delete-next-char",	"n",	TOPLN,	0 },
{	"delete-next-word",	"n",	TOPLN,	0 },
{	"delete-to-end",	0,	TOPLN,	0 },
{	"finish-line",		0,	TOPLN,	0 },
{	"insert-cell-expression",0,	TOPLN,	0 },
{	"insert-cell-value",	0,	TOPLN,	0 },
{	"insert-rel-ref",	0,	TOPLN,	0 },
{	"insert-abs-ref",	0,	TOPLN,	0 },
{	"insert-character",	"T",	TOPLN,  0 },
{			0,	0,	0,	0 }
};

static struct key edit_keys[] = {
	{ 1,	L_BEG_LINE },		/* ^A */
	{ 1,	L_BK_CHR },
	{ 0,	UNBOUND },
	{ 1,	L_FW_DEL_CHR },
	{ 1,	L_END_LINE },		/* ^E */
	{ 1,	L_FW_CHR },
	{ 0,	UNBOUND },
	{ 1,	L_BK_DEL_CHR },
	{ 0,	UNBOUND },		/* ^I */
	{ 1,	L_FINISH },
	{ 1,	L_FW_DEL_END },
	{ 0,	UNBOUND },
	{ 1,	L_FINISH },		/* ^M */
	{ 0,	UNBOUND },
	{ 1,	L_TOGGLE_OVER },
	{ 0,	UNBOUND },
	{ 0,	UNBOUND },		/* ^Q */
	{ 0,	UNBOUND },
	{ 0,	UNBOUND },
	{ 0,	UNBOUND },
	{ 1,	L_BK_DEL_END },		/* ^U */
	{ 0,	UNBOUND },
	{ 0,	UNBOUND },
	{ 0,	UNBOUND },
	{ 0,	UNBOUND },
	{ 0,	UNBOUND },
	{ 255,	3 }
};
static struct key edit_1_keys[] = {
	{ 1,	L_INS_CHR }
};
static struct key edit_2_keys[] = {
	{ 1,	L_BK_DEL_CHR }
};

struct keymap edit_map[] = {
	{
		&edit_map[1],	0,	0,	1,
		CTRL('A'),	CTRL('['),
		&edit_keys[0]
	}, {
		&edit_map[2],	0,	1,	1,
		' ',	'~',
		&edit_1_keys[0]
	},{
		&main_map,	1,	0,	1,
		'\177', '\177',
		&edit_2_keys[0]
	}
};

/* Beginning of the edit meta-keymap  This is a sparse keymap */
static struct key emk3[] = {
	{ 1, L_BK_DEL_WORD }
};
static struct key emk2[] = {
	{ 1, L_INS_REL },
	{ 0, UNBOUND },
	{ 0, UNBOUND },
	{ 0, UNBOUND },
	{ 1, L_INS_VAL }
};
static struct key emk1[] = {
	{ 1, L_INS_ABS },
	{ 1, L_BK_WORD },
	{ 0, UNBOUND },
	{ 1, L_FW_DEL_WORD },
	{ 1, L_INS_EXPR },
	{ 1, L_FW_WORD },
};

struct keymap edit_meta_map[] = {
	{
		&edit_meta_map[1],	0,	0,	0,
		CTRL('H'), CTRL('H'),
		&emk3[0]
	},{
		&edit_meta_map[2],	0,	0,	0,
		'A',	'F',
		&emk1[0]
	},{
		&edit_meta_map[3],	0,	0,	0,
		'R', 'V',
		&emk2[0]
	},{
		&edit_meta_map[4],	0,	0,	0,
		'a',	'f',
		&emk1[0]
	},{
		&edit_meta_map[5],	0,	0,	0,
		'r', 'v',
		&emk2[0]
	},{
		&meta_map[0],		1,	0,	0,
		'\177', '\177',
		&emk3[0]
	}

};

void
open_display FUN1(int,c)
{
	extern unsigned short print_width;

	initscr();
	scrollok(stdscr,0);
#if !defined(__TURBOC__) && !defined(SYSV) && !defined(AMIGA)
	if(c || !strncmp(getenv("TERM"),"xterm",5)) {
		extern int stty EXT2(int, struct sgttyb *);

		_tty.sg_ispeed=15;_tty.sg_ospeed=15;stty(_tty_ch,&_tty);
	}
#endif

	cbreak();
	noecho();
	nonl();

	/* Must be after initscr() */
	print_width=COLS;
	nwin=1;
	wins=cwin=ck_malloc(sizeof(struct window));
	cwin->win_slops=(void *)0;
	wins->win_over=0;
	wins->win_down=3;
	/* wins->win_edges=1; */
	wins->win_flags= WIN_EDGES|WIN_EDGE_REV;
	wins->win_linked= -1;
	wins->numc=COLS;
	wins->numr=LINES-3;
	wins->lh_wid=0;
	curow=wins->curow=MIN_ROW;
	cucol=wins->cucol=MIN_COL;
}

void
close_display FUN0()
{
	clear();
	refresh();
	(void)endwin();
}

void
clear_top_before FUN0()
{
	textout=0;
	if(topclear==2) {
		move(input-1,0);
		clrtoeol();
		topclear=0;
	}
	move(0,0);
}

void
clear_top_after FUN0()
{
	move(input-1,0);
	clrtoeol();
	topclear=0;
}

void
redisp FUN0()
{
	refresh();
}

static int nline;
static int save_auto;
extern int auto_recalc;

void
text_start FUN0()
{
	clear();
	redisp();
	nline=0;
	save_auto=auto_recalc;
	auto_recalc=0;
	redrew++;
}

void
text_line FUN1N(char *,ptr)
{
	va_list ap;
	char sav;
	char buf[1000];

	var_start(ap,ptr);
	vsprintf(buf,ptr,ap);
	va_end(ap);
	ptr=buf;

	for(;;) {
		if(nline==LINES-1) {
			move(nline,0);
			addstr("Press a key to to proceed:  ");
			refresh();
			(void)get_chr();
			clear();
			nline=0;
		}
		move(nline++,0);
		if(strlen(ptr)<=COLS) {
			addstr(ptr);
			break;
		}
		sav=ptr[COLS];
		ptr[COLS]='\0';
		addstr(ptr);
		ptr[COLS]=sav;
		refresh();
		ptr+=COLS;
	}
}
void
text_finish FUN0()
{

	if(nline!=0) {
		move(nline,0);
		addstr("Press a key to continue:  ");
		refresh();
		(void)get_chr();
	}
	auto_recalc=save_auto;
	disp_scrn();
}

/* Return zero on success,
	one on empty input,
	and two on abort */
int
get_inp_line FUN2(char *,prompt, struct line *,line)
{
	static int over;	/* Overwrite or insert */

	char *ptr;		/* Current position in the line */
	char *sptr;		/* Start of the line */
	char *eptr;		/* End of the line */
	int line_len;		/* length of the line */

	int col = 0;		/* Current cursor column */
	int plen;		/* Length of the prompt */

	int domove = 0;
	int doscroll = 0;

	int c;
	int sfilled;

	char *in_str;
	int n;
	int do_free;
	char vbuf[50];
	extern char *macro_func_arg;
	extern struct cmd_func *cur_cmd;
	extern int cur_chr;
	extern unsigned char cur_vector;

#define BUFFER 10

#define MORE(bytes)					\
	line_len+=bytes;				\
	line->buf=ck_realloc(sptr,line_len+1);		\
	eptr= eptr-sptr + line->buf;			\
	ptr= ptr-sptr + line->buf;			\
	sptr= line->buf;				\

#define KILLCH()				\
{						\
	if(ptr!=eptr) {				\
		char *tptr;			\
		for(tptr=ptr;tptr<=eptr;tptr++) \
			tptr[-1]= tptr[0];	\
	}					\
	--ptr;					\
	--eptr;					\
	--sfilled;				\
	--col;					\
	if(col<BUFFER)				\
		doscroll++;			\
	else if(!doscroll) {			\
		move(input-1,col);		\
		delch();			\
		if(eptr-sptr+plen>=COLS && ptr+(COLS-1-col)<eptr) {	\
			mvaddch(input-1,COLS-1,ptr[COLS-1-col]);\
			domove++;		\
		}				\
	}					\
}

	topclear=2;

	if(macro_func_arg) {
		set_line(line,macro_func_arg);
		macro_func_arg=0;
		return 0;
	}
	if(!line->alloc) {
		line->alloc=LINE_MIN;
		line->buf=ck_malloc(LINE_MIN);
		line->buf[0]='\0';
	}
	line_len=line->alloc-1;		/* Leave room for the NULL */
	sptr=line->buf;

	sfilled=strlen(sptr);
	plen=strlen(prompt)+3;
	ptr=sptr+sfilled;
	eptr=ptr;
	over=0;

	doscroll++;
	for(;;) {
		if(domove) {
			domove=0;
			if(col<BUFFER || col>COLS-BUFFER)
				doscroll++;
			else
				move(input-1,col);
		}
		if(doscroll) {
			doscroll=0;
			redrew=0;
			if(textout==2) {
				/* [MORE] the error msg, so the luser has
				   a chance to see it. . . */
				mvaddstr(input-1,COLS-7,"[MORE]");
				(void)get_chr();
			}
			textout=0;
			move(input-1,0);
			clrtoeol();
			if(plen + (ptr-sptr)<COLS-BUFFER) {
				*eptr=0;
				printw("%s:  %.*s",prompt,COLS-plen,sptr);
				col=plen+(ptr-sptr);
			} else {
				char *p;
				p=sptr+COLS-2*BUFFER-plen;
				while(ptr-p>=COLS-BUFFER)
					p+=COLS-2*BUFFER;
				*eptr=0;
				printw("%.*s",COLS,p);
				col=ptr-p;
			}
			move(input-1,col);
		}
		if(how_many!=1) {
			how_many=1;
			cur_status();
			move(input-1,col);
		}
		map_chr(EDIT_MAP);
 swtch:
		if(cur_vector!=1) {
			n=global_cmd(EDIT_MAP);
			if(redrew || textout)
				doscroll++;
			if(n==-3) {
				*eptr++='\0';
				return 2;
			} else if(n==-2) {
				beep();
				beep();
			} else if(n>=0)
				goto swtch;
			continue;
		}
		switch(cur_cmd-edit_funcs) {
		case L_BEG_LINE:
			col-=ptr-sptr;
			ptr=sptr;
			domove++;
			break;

		case L_BK_CHR:
			for(n=0;ptr!=sptr && n<how_many;n++) {
				--ptr;
				--col;
			}
			domove++;
			break;

		case L_BK_WORD:
			for(n=0;ptr!=sptr && n<how_many;n++) {
				while(ptr!=sptr && !isalnum(ptr[-1])) {
					--ptr;
					--col;
				}
				while(ptr!=sptr && isalnum(ptr[-1])) {
					--ptr;
					--col;
				}
			}
			domove++;
			break;

		case L_FW_DEL_CHR:
			for(n=0;ptr!=eptr && n<how_many;n++) {
				ptr++;
				col++;
				KILLCH()
			}
			break;

		case L_FW_DEL_WORD:
			for(n=0;ptr!=eptr && n<how_many;n++) {
				while(ptr!=eptr && !isalnum(ptr[0])) {
					ptr++;
					col++;
					KILLCH()
				}
				while(ptr!=eptr && isalnum(ptr[0])) {
					ptr++;
					col++;
					KILLCH()

				}
			}
			break;

		case L_END_LINE:
			col+=eptr-ptr;
			ptr=eptr;
			domove++;
			break;

		case L_FW_CHR:
			for(n=0;ptr!=eptr && n<how_many;n++) {
				col++;
				ptr++;
			}
			domove++;
			break;

		case L_FW_WORD:
			for(n=0;ptr!=eptr && n<how_many;n++) {
				while(ptr!=eptr && !isalnum(ptr[0])) {
					col++;
					ptr++;
				}
				while(ptr!=eptr && isalnum(ptr[0])) {
					col++;
					ptr++;
				}
			}
			domove++;
			break;

		case L_FW_DEL_END:
			eptr=ptr;
			clrtoeol();
			break;

		case L_TOGGLE_OVER:
			over= !over;
			break;

		case L_BK_DEL_WORD:
			for(n=0;ptr!=sptr && n<how_many;n++) {
				while(ptr!=sptr && !isalnum(ptr[-1]))
					KILLCH()

				while(ptr!=sptr && isalnum(ptr[-1]))
					KILLCH()
			}

			break;

		case L_BK_DEL_END:
			while(ptr!=sptr)
				KILLCH()
			break;

		case L_BK_DEL_CHR:
			for(n=0;ptr!=sptr && n<how_many;n++)
				KILLCH()
			break;


		case L_FINISH:
			*eptr++='\0';
			textout=1;
			return eptr-sptr==1 ? 1 : 0;

		case L_INS_EXPR:
		{
			CELL *cp;

			if(!(cp=find_cell(curow,cucol)))
				break;
			in_str=decomp(curow,cucol,cp);
			do_free=1;
		}
			goto insert_string;

		case L_INS_VAL:
			in_str=cell_value_string(curow,cucol);
			do_free=0;
			goto insert_string;

		case L_INS_REL:
#ifdef A0_REFS
			if(mkrow!=NON_ROW) {
				struct rng r;

				set_rng(&r,curow,cucol,mkrow,mkcol);
				in_str=range_name(&r);
			} else
				in_str=cell_name(curow,cucol);
#else
			if(mkrow!=NON_ROW) {
				switch(   ((curow==setrow)<<3)
					+ ((mkrow==setrow)<<2)
 					+ ((cucol==setcol)<<1)
 					+ (mkcol==setcol)) {
				case 0:
				case 1:
				case 2:
				case 4:
				case 5:
				case 6:
				case 8:
				case 9:
				case 10:
					sprintf(vbuf,"r[%+d:%+d]c[%+d:%+d]",
						(curow < mkrow ? curow : mkrow) - setrow,
						(curow < mkrow ? mkrow : curow) - setrow,
						(cucol < mkcol ? cucol : mkcol) - setcol,
						(cucol < mkcol ? mkcol : cucol) - setcol);
					break;
				case 3:
				case 7:
				case 11:
					sprintf(vbuf,"r[%+d:%+d]c",
						(curow < mkrow ? curow : mkrow) - setrow,
						(curow < mkrow ? mkrow : curow) - setrow);
					break;
				case 12:
				case 14:
				case 13:
					sprintf(vbuf,"rc[%+d:%+d]",
						(cucol < mkcol ? cucol : mkcol) - setcol,
						(cucol < mkcol ? mkcol : cucol) - setcol);
					break;
				case 15:
					strcpy(vbuf,"rc");
					break;
#ifdef TEST
				default:
					panic("Unknown value");
#endif
				}
			} else {
				switch(((curow==setrow)<<1) + (cucol==setcol)) {
				case 0:
					sprintf(vbuf,"r[%+d]c[%+d]",curow-setrow,cucol-setcol);
					break;
				case 1:
					sprintf(vbuf,"r[%+d]c",curow-setrow);
					break;
				case 2:
					sprintf(vbuf,"rc[%+d]",cucol-setcol);
					break;
				case 3:
					strcpy(vbuf,"rc");
					break;
#ifdef TEST
				default:
					panic("huh what");
#endif
				}
			}
			in_str=vbuf;
#endif
			do_free=0;
			goto insert_string;

		case L_INS_ABS:
			/* Insert current cell/range name as an absolute reference */
#ifdef A0_REFS
			if(mkrow!=NON_ROW)
				sprintf(vbuf,"$%s$%u:$%s:$%u",col_to_str(cucol),curow,col_to_str(mkcol),mkrow);
			else
				sprintf(vbuf,"$%s$%u",col_to_str(cucol), curow);
			in_str=vbuf;
#else
			if(mkrow!=NON_ROW) {
				struct rng r;

				set_rng(&r,curow,cucol,mkrow,mkcol);
				in_str=range_name(&r);
			} else
				in_str=cell_name(curow,cucol);
#endif
			do_free=0;

		insert_string:
			c=strlen(in_str);
			if((sfilled + (over ? c + (ptr-eptr) : c)) >= line_len) {
				n= over ? c + (ptr-eptr) : c;
				if(n<LINE_MIN)
					n=LINE_MIN;
				MORE(n);
			}
			if(over) {
				if(ptr+c>eptr) {
					sfilled+=(ptr+c)-eptr;
					eptr=ptr+c;
				}
			} else {
				if(ptr!=eptr) {
					char *tptr;

					for(tptr=eptr;tptr>=ptr;--tptr)
						tptr[c]=tptr[0];
				}
				eptr+=c;
				sfilled+=c;
			}
			bcopy(in_str,ptr,c);
			ptr+=c;
			if(col+c>=COLS-BUFFER) {
				doscroll++;
			} else {
				col+=c;
				if(!over && ptr!=eptr) {
#ifdef __TURBOC__
					movetext(col-c,1,COLS-c,1,col,1);
#else
					while(c--)
						insch(' ');
#endif
				}
				addstr(in_str);
			}
			if(do_free)
				decomp_free();
			break;

		case L_INS_CHR:
			if((sfilled+how_many + (over ? (ptr-eptr) : (0))) >= line_len) {
				n = over ? how_many+(ptr-eptr) : how_many;
				if(n<LINE_MIN)
					n=LINE_MIN;
				MORE(n);
			}
			if(over) {
				if(ptr+how_many>eptr) {
					sfilled+=(ptr+how_many)-eptr;
					eptr=ptr+how_many;
				}
			} else {
				if(ptr!=eptr) {
					char *tptr;

					for(tptr=eptr;tptr>=ptr;--tptr)
						tptr[how_many]=tptr[0];
				}
				eptr+=how_many;
				sfilled+=how_many;
			}
			for(n=0;n<how_many;n++)
				*ptr++=cur_chr;

			if(col+how_many>=COLS-BUFFER) {
				doscroll++;
			} else if(over || ptr==eptr) {
				col+=how_many;
				for(n=0;n<how_many;n++)
					addch(cur_chr);
			} else {
				col+=how_many;
				for(n=0;n<how_many;n++) {
					insch(cur_chr);
					move(input-1,col);
				}
			}
			break;
		}
	}
#undef KILLCH
#undef BUFFER
#undef MORE
}

/* External interface to window routines */
/* Create a new window by splitting the current one. */
void
open_window FUN1(char *,text)
{
	int hv;
	int where;
	int tmp;
	struct window *win;

	while(*text==' ')
		text++;

	if(*text=='h' || *text=='H') {
		hv=1;
	} else if(*text=='v' || *text=='V') {
		hv=0;
	} else {
		error_msg("Open 'h'orizontal or 'v'ertical window, not '%s'",text);
		return;
	}
	where=atoi(text+1);

	if(   (!hv && (    where<MIN_WIN_WIDTH
			|| cwin->numc-where<MIN_WIN_WIDTH))
	   || ( hv && (    where<MIN_WIN_HEIGHT
			|| cwin->numr-where<MIN_WIN_HEIGHT))) {
		error_msg("Window won't fit!");
		return;
	}
	nwin++;
	tmp=cwin-wins;

	wins=ck_realloc(wins,nwin*sizeof(struct window));
	win= &wins[nwin-1];
	win->win_slops=(void *)0;
	cwin= &wins[tmp];
	win->win_over=cwin->win_over + (hv ? 0 : where) - cwin->lh_wid;
	win->win_down=cwin->win_down + (hv ? where : 0);
	win->win_flags = cwin->win_flags;
	win->win_linked= -1;
	win->lh_wid=0;
	win->numc=cwin->numc + cwin->lh_wid + (hv ? 0 : -where);
	win->numr=cwin->numr + (hv ? -where : 0);
	win->curow=curow;
	win->cucol=cucol;
	set_numcols(win,curow);
	cwin->numc-=(hv ? 0 : win->numc+win->lh_wid);
	cwin->numr-=(hv ? (win->numr+(win->lh_wid?1:0)) : 0);
	cwin->curow=curow;
	cwin->cucol=cucol;
	hide_cell_cursor();
	win=cwin;
	cwin= &wins[nwin-1];
	recenter_window(cwin);
	recenter_window(win);
	disp_scrn();
}

static void do_close_window FUN1(int, num)
{
	int n;
	struct window *win,*kwin;
	int nlf,nrt,nup,nbl;
	int klo,kho,kld,khd;
	int lo,ho,ld,hd;
	struct tmp {
		int	l,
			r,
			u,
			b;
	} *tmpptr;
	void recenter_all_win EXT0();

	tmpptr=ck_malloc(sizeof(struct tmp)*nwin);

	kwin= &wins[num];
	nlf=nrt=nup=nbl=0;
	klo=kwin->win_over-kwin->lh_wid;
	kho=kwin->win_over+kwin->numc-1;
	kld=kwin->win_down-(kwin->lh_wid?1:0);
	khd=kwin->win_down+kwin->numr-1;

	for(win=wins;win<&wins[nwin];win++) {
		lo=win->win_over-win->lh_wid;
		ho=win->win_over+win->numc-1;
		ld=win->win_down-(win->lh_wid?1:0);
		hd=win->win_down+win->numr-1;

		/* Match to the left ? */
		if(lo==kho+1) {
			if(ld>=kld && hd<=khd)
				tmpptr[nrt++].r=win-wins;
			else if(hd>=kld && ld<=khd)
				nrt= nwin;
		}
		else if(ho==klo-1) {
			if(ld>=kld && hd<=khd)
				tmpptr[nlf++].l=win-wins;
			else if(hd>=kld && ld<=khd)
				nlf= nwin;
		}
		else if(ld==khd+1) {
			if(lo>=klo && ho<=kho)
				tmpptr[nbl++].b=win-wins;
			else if(ho>=kho && lo<=kho)
				nbl= nwin;
		}
		else if(hd==kld-1) {
			if(lo>=klo && ho<=kho)
				tmpptr[nup++].u=win-wins;
			else if(ho>=kho && lo<=kho)
				nup= nwin;
		}

	}
	if(nrt==0)
		nrt=nwin;
	if(nlf==0)
		nlf=nwin;
	if(nbl==0)
		nbl=nwin;
	if(nup==0)
		nup=nwin;
	if(nrt<=nlf && nrt<=nbl && nrt<=nup) {
		for(n=0;n<nrt;n++) {
			wins[tmpptr[n].r].numc+=kwin->lh_wid+kwin->numc;
			wins[tmpptr[n].r].win_over-=kwin->lh_wid+kwin->numc;
		}
	} else if(nlf<=nbl && nlf<=nup) {
		for(n=0;n<nlf;n++)
			wins[tmpptr[n].l].numc+=kwin->lh_wid+kwin->numc;
	} else if(nbl<=nup) {
		for(n=0;n<nbl;n++) {
			wins[tmpptr[n].b].numr+=kwin->numr+(kwin->lh_wid ? 1 : 0);
			wins[tmpptr[n].b].win_down-=kwin->numr+(kwin->lh_wid ? 1 : 0);
		}
	} else {
		for(n=0;n<nup;n++)
			wins[tmpptr[n].u].numr+=kwin->numr+(kwin->lh_wid ? 1 : 0);
	}
 	if(kwin==cwin && kwin!= wins)
		--cwin;
	if(cwin==&wins[nwin-1])
		--cwin;
	while(kwin< &wins[nwin]) {
		*kwin= kwin[1];
		kwin++;
	}
	--nwin;
	recenter_all_win();
	return;
}

void
close_window FUN1(char *,text)
{
	int num;

	num=atoi(text)-1;
	if(num<0 || num>=nwin) {
		error_msg("Window %s?",text);
		return;
	}
	if(nwin==1) {
		error_msg("You can't close the last window!");
		return;
	}
	do_close_window(num);
}

void
goto_window FUN1(char *,text)
{
	int n;

	n=atoi(text)-1;
	if(n<0 || n>nwin) {
		error_msg("Window %s doesn't exist.",text);
		return;
	}
	hide_cell_cursor();
	cwin->curow=curow;
	cwin->cucol=cucol;
	cwin= &wins[n];
	curow=cwin->curow;
	cucol=cwin->cucol;
	display_cell_cursor();
}

static int locmp FUN2(struct window **,a, struct window **,b)
{
	return (*a)->win_down-(*b)->win_down;
}
static int hicmp FUN2(struct window **,a, struct window **,b)
{
	return ((*a)->win_down+(*a)->numr)-((*b)->win_down+(*b)->numr);
}

/* How this works:  First figure out how many lines are now avaliable for
   sheet data.  Then squeeze/stretch the windows to fit that space. . . */

static void resize_screen FUN0()
{
	int n;
	int nlines;
	int firstln;
	/* int shifted; */
	int lo,sq,hi;
	struct window **t;

	if(input<LINES/2) {
		if(status>LINES/2) {
			nlines=status-input-1;
			firstln=input+1;
		} else {
			firstln=1+input>status?input:status;
			nlines=LINES-firstln;
		}
	} else if(status<LINES/2) {
		nlines=input-status;
		firstln=status+1;
	} else {
		firstln=1;
		nlines=(input>status?status:input)-1;
	}

	t=ck_malloc(nwin*sizeof(struct window *));
	for(n=0;n<nwin;n++)
		t[n]= &wins[n];
	qsort(t,nwin,sizeof(struct window *),hicmp);
	hi=t[0]->win_down+t[0]->numr;
	sq=firstln+nlines-hi;
	for(n=0;n<nwin && hi==t[n]->win_down+t[0]->numr;n++) {
		t[n]->numr-=sq;
	}
	qsort(t,nwin,sizeof(struct window *),locmp);
	lo=t[0]->win_down;
	sq=firstln-lo+((t[0]->win_flags&WIN_EDGES)?1:0);
	for(n=0;n<nwin && t[n]->win_down==lo;n++) {
		t[n]->numr-=sq;
		t[n]->win_down=firstln+((t[0]->win_flags&WIN_EDGES)?1:0);
	}
	cwin->curow=curow;
	cwin->cucol=cucol;
	for(n=0;n<nwin;n++) {
		if(wins[n].numr<=0) {
			do_close_window(n);
			n--;
		} else
			recenter_window(&wins[n]);
	}
	disp_scrn();
}

#if 0
static void shift_screen FUN1(int, by)
{
	int n;

	for(n=0;n<nwin;n++)
		wins[n].win_down+=by;
	disp_scrn();
}
#endif

int
set_window_option FUN2(int,set_opt, char *,text)
{
	int n;
	static struct opt {
		char *text;
		int bits;
	} opts[] = {
		{ "reverse",	WIN_EDGE_REV },
		{ "standout",	WIN_EDGE_REV },
		{ "page",	WIN_PAG_HZ|WIN_PAG_VT },
		{ "pageh",	WIN_PAG_HZ },
		{ "pagev",	WIN_PAG_VT },
		{ "lockh",	WIN_LCK_HZ },
		{ "lockv",	WIN_LCK_VT }
	};
	if(set_opt && !strincmp(text,"status ",7)) {
		n=atoi(text+7);
		if(n>LINES || n<-LINES) {
			error_msg("%s?",text);
			return 1;
		}
		if(n<0)
			n=LINES+1+n;
		status=n;
		resize_screen();
	} else if(set_opt && !strincmp(text,"input ",6)) {
		n=atoi(text+6);
		if(n==0 || n>LINES || n<-LINES) {
			error_msg("%s?",text);
			return 1;
		}
		if(n<0)
			n=LINES+1+n;
		input=n;
		resize_screen();
	} else if(!strincmp(text,"link",4)) {
		if(set_opt) {
			n=atoi(text+4)-1;
			if(n<0 || n>nwin)
				error_msg("Can't '%s': window # out of range",text);
			else
				cwin->win_linked= n;
		} else
			cwin->win_linked= -1;
	} else if(set_opt && !stricmp(text,"unlink")) {
		cwin->win_linked= -1;
	} else if(!stricmp(text,"edges")) {
		if(set_opt) {
			if((cwin->win_flags&WIN_EDGES)==0) {
				if(cwin->numr<2 || cwin->numc<6)
					error_msg("Edges wouldn't fit!");
				cwin->win_flags|= WIN_EDGES;
				cwin->win_down++;
				cwin->numr--;
				/* set_numcols(cwin,cwin->screen.hr); */
			}
		} else {
			if(cwin->win_flags&WIN_EDGES) {
				cwin->win_flags&= ~WIN_EDGES;
				cwin->win_over-=cwin->lh_wid;
				cwin->numc+=cwin->lh_wid;
				cwin->lh_wid=0;
				cwin->win_down--;
				cwin->numr++;
			}
		}

	} else if(set_opt && !strincmp(text,"row ",4)) {
		text+=4;
		curow=astol(&text);
	} else if(set_opt && !strincmp(text,"col ",4)) {
		text+=4;
		cucol=astol(&text);
	} else {
		for(n=0;n<sizeof(opts)/sizeof(struct opt);n++) {
			if(!stricmp(text,opts[n].text)) {
				if(set_opt)
					cwin->win_flags|= opts[n].bits;
				else
					cwin->win_flags&= ~opts[n].bits;
				break;
			}
		}
		if(n==sizeof(opts)/sizeof(struct opt))
			return 0;
	}
	return 1;
}

void
show_window_options FUN0()
{
	int n;
	struct window *win;

	cwin->curow=curow;
	cwin->cucol=cucol;
	win= &wins[0];
	if(status)
		text_line("Status line at %d", status);
	else
		text_line("Status line disabled.");
	text_line("");
	for(n=0;n<nwin;n++) {
		text_line("Window #%d showing %s, with cursor at %s",
			n+1,
			range_name(&(win->screen)),
			cell_name(win->curow,win->cucol));
		text_line("   Options:  %sedges (%sreverse)%s%s%s%s",
			win->win_flags&WIN_EDGES    ? "" : "no",
			win->win_flags&WIN_EDGE_REV ? "" : "no",
			win->win_flags&WIN_PAG_HZ   ? ", pageh" : "",
			win->win_flags&WIN_PAG_VT   ? ", pagev" : "",
			win->win_flags&WIN_LCK_HZ   ? ", lockh" : "",
			win->win_flags&WIN_LCK_VT   ? ", lockv" : "");
		if(win->win_linked!=-1)
			text_line("Linked to window %d",win->win_linked+1);
		win++;
	}
}

#ifdef TEST
void
dbg_show_wins FUN0()
{
	int n;

	for(n=0;n<nwin;n++) {
		text_line("Window %d",n);
		text_line("  screen %s  slops %p  flags %x  linked %d",
 range_name(&(wins[n].screen)),wins[n].win_slops,wins[n].win_flags,
 wins[n].win_linked);
		text_line("  ov %d  dn %d  nr %d  nc %d  lh_wid %d  pos %s",wins[n].win_over,wins[n].win_down,wins[n].numr,wins[n].numc,wins[n].lh_wid,cell_name(wins[n].curow,wins[n].cucol));
		text_line("");
	}
}
#endif
		
void
disp_scrn FUN0()
{
	CELLREF cc,rr;
	int n,n1;
	CELL *cp;
	struct window *win;

	clear();
	redrew++;
	for(win=wins;win<&wins[nwin];win++) {
		if(win->lh_wid) {
			move(win->win_down-1,win->win_over-win->lh_wid);
			printw("#%*d ", win->lh_wid-2, 1+win-wins);
			if(win->win_flags&WIN_EDGE_REV)
				standout();
			cc=win->screen.lc;
			do {
				n=get_width(cc);
				if(n>win->numc)
					n=win->numc;
				if(n>1) {
					char *ptr;
#ifdef A0_REFS
					ptr=col_to_str(cc);
#else
					char buf[30];

					sprintf(buf,"C%u",cc);
					ptr=buf;
#endif
					--n;
					n1=strlen(ptr);
					if(n<n1)
						printw("%.*s ",n,"###############");
					else {
						n1=(n-n1)/2;
						printw("%*s%-*s ",n1,"",n-n1,ptr);
					}
				} else if(n==1)
					addstr("#");
			} while(cc++<win->screen.hc);

			rr=win->screen.lr;
			do {
				move(rr+win->win_down-win->screen.lr,win->win_over-win->lh_wid);
#ifdef A0_REFS
				printw("%-*d ",win->lh_wid-1,rr);
#else
				printw("R%-*d",win->lh_wid-1,rr);
#endif
			} while(rr++<win->screen.hr);

			if(win->win_flags&WIN_EDGE_REV)
				standend();
		}
		flush_slops(win->win_slops);
		find_cells_in_range(&(win->screen));
		while(cp=next_row_col_in_range(&rr,&cc))
			if(GET_TYP(cp))
			  {
			    pr_cell_win(win,rr,cc,cp);
			  }
	}
	if(!(cp=find_cell(curow,cucol)) || !GET_TYP(cp))
		display_cell_cursor();
	cur_status();
}

void
set_numcols FUN2(struct window *,win, CELLREF,hr)
{
	int lh;

	if((win->win_flags&WIN_EDGES)==0)
		lh=0;
#if BITS_PER_CELLREF>8
	else if((win->win_flags&WIN_PAG_HZ) || hr>=10000)
		lh=7;
	else if(hr>=1000)
		lh=6;
	else if(hr>=100)
		lh=5;
#else
	else if((win->win_flags&WIN_PAG_HZ) || hr>=100)
		lh=5;
#endif
	else if(hr>10)
		lh=4;
	else
		lh=3;
	win->win_over-=win->lh_wid-lh;
	win->numc+=win->lh_wid-lh;
	win->lh_wid=lh;
}

static void
move_cursor_to FUN3(struct window *,win, CELLREF, r,CELLREF, c)
{
	int cc;
	int cursor_col;

	cursor_col=win->win_over;
	for(cc=win->screen.lc;cc<c;cc++)
		cursor_col+=get_width(cc);
	move(win->win_down+r-win->screen.lr,cursor_col);
}

void
pr_cell FUN3(CELLREF, r, CELLREF, c, CELL *, cp)
{
	int xx,yy;
	struct window *win = (void *)0;

	getyx(stdscr,yy,xx);
	for(win=wins;win<&wins[nwin];win++) {
		if(   r<win->screen.lr || r>win->screen.hr
	 	   || c<win->screen.lc || c>win->screen.hc)
			continue;

		pr_cell_win(win,r,c,cp);
	}
	move(yy,xx);
}

static void
pr_cell_win FUN4(struct window *,win, CELLREF, r, CELLREF, c, CELL *, cp)
{
	int glowing;
	int lenstr;
	int j;
	int wid,wwid;
	char *ptr;

	wid=get_width(c);
	if(!wid)
		return;
	if(wid>win->numc)
		wid=win->numc;
	glowing = (r==curow && c==cucol && win==cwin);

	ptr=print_cell(cp);
#ifdef TEST
	if(!ptr)
		panic("print_cell returned 0");
#endif
	move_cursor_to(win,r,c);
	if(glowing)
		standout();
	j=GET_JST(cp);
	if(j==JST_DEF)
		j=default_jst;
	lenstr=strlen(ptr);

	if(lenstr<=wid-1) {
		CELLREF ccl,cch;

		if(j==JST_LFT)
			printw("%-*.*s",wid, wid-1,ptr);
		else if(j==JST_RGT)
			printw("%*.*s ",wid-1, wid-1,ptr);
		else if(j==JST_CNT) {
			wid=(wid-1)-lenstr;
			printw("%*s%*s ",(wid+1)/2+lenstr,ptr,wid/2,"");
		}
#ifdef TEST
		else
			panic("Unknown justification");
#endif
		if(glowing)
			standend();

		if(lenstr==0 && c>win->screen.lc && find_slop(win->win_slops,r,c-1,&ccl,&cch)) {
			CELLREF ccdl,ccdh;

			if(find_slop(win->win_slops, r,c,&ccdl,&ccdh) && ccdl==c) {
				kill_slop(win->win_slops, r,ccdl,ccdh);
				for(;ccdh!=ccdl;--ccdh)
					if(ccdh!=c && (wid=get_width(ccdh))) {
						move_cursor_to(win,r,ccdh);
						printw("%*s",wid,"");
					}
			}
			kill_slop(win->win_slops, r,ccl,cch);
			pr_cell(r,ccl,find_cell(r,ccl));
		} else if(find_slop(win->win_slops, r,c,&ccl,&cch)) {
			kill_slop(win->win_slops, r,ccl,cch);
			for(;cch!=ccl;--cch)
				if(cch!=c && (wid=get_width(cch))) {
					move_cursor_to(win,r,cch);
						printw("%*s",wid,"");
				}
			pr_cell(r,ccl,find_cell(r,ccl));
		}
	} else {
		CELLREF cc=c;
		CELL *ccp;
		CELLREF ccl,cch;

		for(wwid=wid;lenstr>wwid-1;wwid+=get_width(cc)) {
			if(   ++cc>win->screen.hc
			   || (   ccp=find_cell(r,cc))
			       && GET_TYP(ccp)
			       && (   GET_FMT(ccp)!=FMT_HID
				   || (   GET_FMT(ccp)==FMT_DEF
				       && default_fmt!=FMT_HID))) {
				--cc;
				break;
			}
		}

		if(lenstr>wwid-1)
 			if(GET_TYP(cp)==TYP_FLT)
				ptr=adjust_prc(ptr,cp,wwid-1,wid-1,j);
			else if(GET_TYP(cp)==TYP_INT)
				ptr=(char *)numb_oflo;

		if(wwid==1) {
			addch(' ');
			if(glowing)
				standend();
		} else if(wwid==wid) {
			printw("%-*.*s ",wwid-1,wwid-1,ptr);
			if(glowing)
				standend();
		} else if(glowing) {
			printw("%.*s",wid,ptr);
			standend();
			printw("%-*.*s ",wwid-wid-1,wwid-wid-1,ptr+wid);
		} else if(r==curow && (cucol>c && cucol<=cc)) {
			CELLREF ctmp;
			int w_left;
			int w_here;

			w_left=wid;
			for(ctmp=c+1;ctmp<cucol;ctmp++)
				w_left+=get_width(ctmp);
			printw("%.*s",w_left,ptr);
			standout();
			w_here=get_width(cucol);
			if(wwid>w_left+w_here) {
				printw("%-*.*s",w_here,w_here,ptr+w_left);
				standend();
				printw("%-*.*s ",wwid-(w_left+w_here)-1,wwid-(w_left+w_here)-1,ptr+w_left+w_here);
			} else {
				printw("%-*.*s",w_here,w_here-1,ptr+w_left);
				standend();
			}
		} else
			printw("%-*.*s ",wwid-1,wwid-1,ptr);

		if(find_slop(win->win_slops, r,c,&ccl,&cch)) {
			change_slop(win->win_slops, r,ccl,cch,c,cc);
			for(;cch>cc;--cch)
				if(wid=get_width(cch)) {
					move_cursor_to(win,r,cch);
					printw("%*s",wid,"");
				}
			for(cch=c-1;cch>ccl;--cch)
				if(wid=get_width(cch)) {
					move_cursor_to(win,r,cch);
					printw("%*s",wid,"");
				}
			if(ccl!=c)
				pr_cell(r,ccl,find_cell(r,ccl));
		} else
			set_slop((VOIDSTAR *)(&(win->win_slops)), r,c,cc);
	}
	if(glowing)
		cur_status();
}
void
cur_status FUN0()
{
	CELL *cp;
	char *dec;
	char *ptr;
	static char hmbuf[40];
	int wid;
	int plen;
	int dlen;
	int yy,xx;

	if(!status)
		return;
	getyx(stdscr,yy,xx);
	move(status-1,0);
	wid=COLS-2;

	if(mkrow!=NON_ROW) {
		struct rng r;

		addch('*');
		--wid;
		set_rng(&r,curow,cucol,mkrow,mkcol);
		ptr=range_name(&r);
	} else
		ptr=cell_name(curow,cucol);

	addstr(ptr);
	wid-=strlen(ptr);

	if(how_many!=1) {
		sprintf(hmbuf," {%u}",how_many);
		addstr(hmbuf);
		wid-=strlen(hmbuf);
	}

	if((cp=find_cell(curow,cucol)) && cp->cell_formula) {
		dec=decomp(curow,cucol,cp);
		dlen=strlen(dec);
	} else {
		dec=0;
		dlen=0;
	}

	ptr=cell_value_string(curow,cucol);
	plen=strlen(ptr);

	if(dec) {
		wid-=4;
		if(dlen+plen>wid) {
			if(plen+3>wid)
				printw(" %.*s... [...]",wid-6,ptr);
			else
				printw(" %s [%.*s...]",ptr,wid-plen-3,dec);
		} else
			printw(" %s [%s]",ptr,dec);
		decomp_free();
	} else if(plen) {
		--wid;
		if(plen>wid)
			printw(" %.*s...",wid-3,ptr);
		else
			printw(" %s",ptr);
	}

	clrtoeol();
	move(yy,xx);
}

static void
display_cell_cursor FUN0()
{
	int cc;
	int cursor_row;
	int cursor_col;
	int cwid;
#ifndef __TURBOC__
	int n;
#endif

	cursor_col=cwin->win_over;
	for(cc=cwin->screen.lc;cc<cucol;cc++)
	  {
	    cursor_col+=get_width(cc);
	  }
	cursor_row=cwin->win_down+curow-cwin->screen.lr;
	cwid=get_width(cucol);
	if(cwid>cwin->numc)
		cwid=cwin->numc;
#ifdef __TURBOC__
	hack_standout(1,cursor_row,cursor_col,cwid);
#else
	move(cursor_row,cursor_col);
	standout();
	for(n=cwid;n;n--)
#ifdef A_STANDOUT
		addch(inch()|A_STANDOUT);
#else
		addch(inch());
#endif
	standend();
#endif
}

static void
hide_cell_cursor FUN0()
{
	int cc;
	int cursor_row;
	int cursor_col;
	int cwid;
#ifndef __TURBOC__
	int n;
#endif

	cursor_col=cwin->win_over;
	for(cc=cwin->screen.lc;cc<cucol;cc++)
	  {
	    cursor_col+=get_width(cc);
	  }
	cursor_row=cwin->win_down+curow-cwin->screen.lr;
	cwid=get_width(cucol);
	if(cwid>cwin->numc)
		cwid=cwin->numc;
#ifdef __TURBOC__
	hack_standout(0,cursor_row,cursor_col,cwid);
#else
	move(cursor_row,cursor_col);
	for(n=cwid;n;n--)
#ifdef SYSV
		addch(inch()&~A_STANDOUT);
#else
		addch(inch());
#endif
#endif
}

#ifdef __TURBOC__
static void
hack_standout FUN4(int,standp, int,rown, int,coln, int,cols)
{
	char buf[COLS*2];
	char *ptr;
	int n;

	standp = standp ? 0x7<<4 : 0x7;
	rown++;
	coln++;
	gettext(coln,rown,coln+cols-1,rown,buf);
	ptr=buf+1;
	n=cols;
	while(n--) {
		*ptr= standp;
		ptr+=2;
	}
	puttext(coln,rown,coln+cols-1,rown,buf);
}

static void
printw FUN1N(char *,str)
{
	va_list ap;
	char text[COLS+1];

	var_start(ap,str);
	vsprintf(text,str,ap);
	addstr(text);
	va_end(ap);
}

#endif

int
get_chr_prompt FUN1(char *,prompt)
{
	mvaddstr(input-1,0,prompt);
	clrtoeol();
	topclear=2;
	refresh();
	return get_chr();
}

void
error_msg FUN1N(char *,str)
{
	va_list foo;
	char buf[1000];
	/* Sigh.  What I'd give for vprintw() */

#ifdef TEST
	extern int isatty();

	if(!dbg_do_stderr)
	dbg_do_stderr= isatty(fileno(stderr)) ? 1 : 2;
#endif
	var_start(foo,str);
	vsprintf(buf,str,foo);
#ifdef TEST
	if(dbg_do_stderr==2) {
		fputs(buf,stderr);
		putc('\n',stderr);
		fflush(stderr);
	}
#endif
	if(textout==2) {
		mvaddstr(input-1,COLS-7,"[MORE]");
		(void)get_chr();
	} else
		textout=2;
	mvaddstr(input-1,0,buf);
	clrtoeol();
	topclear=1;
	refresh();
}

void
info_msg FUN1N(char *,str)
{
	va_list foo;
	char buf[1000];
	/* See previous comment */

	var_start(foo,str);
	vsprintf(buf,str,foo);
	if(!status) {
		if(textout==2) {
			mvaddstr(input-1,COLS-7,"[MORE]");
			(void)get_chr();
		}
		textout=1;
		mvaddstr(input-1,0,buf);
	} else		
		mvaddstr(status-1,0,buf);
	clrtoeol();
	refresh();
}
#ifndef amiga

void
_putchar FUN1(int, ch)
{
	(void)putc(ch,stdout);
	/* (void)putchar(ch); */
}
#endif

#ifndef __TURBOC__
static void
beep FUN0()
{
#ifdef SYSV
	_putchar('\007');
#else
	static char *vb;
	static called;

	if(!called) {
		called++;
		vb=getcap("vb");
	}
	if(vb) {
		_puts(vb);
	} else {
		_putchar('\007');
	}
#endif
}
#endif

void
shift_linked_window FUN2(long,dn, long,ov)
{
	struct window *win;

	win=cwin;
	while(win->win_linked!=-1) {
		win= &wins[win->win_linked];
		if(win==cwin)	/* Loop check! */
			return;
		if((win->win_flags&WIN_LCK_VT)==0)
			win->curow+=dn;
		if((win->win_flags&WIN_LCK_HZ)==0)
			win->cucol+=ov;
		if(   win->curow<win->screen.lr || win->curow>win->screen.hr
		   || win->cucol<win->screen.lc || win->cucol>win->screen.hc)
			recenter_window(win);
	}
}

int
move_cell_cursor FUN2(CELLREF,rr, CELLREF,cc)
{
	int ret;
	int cx,cy;

	if(cwin->win_linked!=-1)
		shift_linked_window((long)rr-curow,(long)cc-cucol);
	if(   rr<cwin->screen.lr || rr>cwin->screen.hr
	   || cc<cwin->screen.lc || cc>cwin->screen.hc) {
		cwin->curow=curow=rr;
		cwin->cucol=cucol=cc;
		recenter_window(cwin);
		disp_scrn();
		ret=1;
	} else {
		getyx(stdscr,cy,cx);
		hide_cell_cursor();
		curow=rr;
		cucol=cc;
		display_cell_cursor();
		cur_status();
		move(cy,cx);
		ret=0;
	}
	if(get_width(cucol)==0)
	 		find_nonzero_width(cwin);
	return ret;
}

static void
find_nonzero_width FUN1(struct window *,win)
{
	CELLREF cc;
	unsigned short n;

	cc=cucol;

	if(cc<win->screen.hc) {
		cc++;
		while((n=get_width(cc))==0) {
			if(cc==win->screen.hc)
				break;
			cc++;
		}
		if(n) {
			cucol=cc;
			return;
		}
	}
	if(cc>win->screen.lc) {
	 		--cc;
		while((n=get_width(cc))==0) {
			if(cc==win->screen.lc)
				break;
			--cc;
		}
		if(n) {
			cucol=cc;
			return;
		}
	}
}


void
recenter_all_win FUN0()
{
	int n;

	cwin->curow=curow;
	cwin->cucol=cucol;
	for(n=0;n<nwin;n++)
		recenter_window(&wins[n]);
	disp_scrn();
}

void
recenter_cur_win FUN0()
{
	cwin->curow=curow;
	cwin->cucol=cucol;
	recenter_window(cwin);
	disp_scrn();
}

static void
recenter_window FUN1(struct window *,win)
{
	CELLREF lr,hr,lc,hc;
	CELLREF rr;
	unsigned short w,ww;
	unsigned short n;
	unsigned short totwid = 0;
	int more;

	if(get_width(win->cucol)==0)
		find_nonzero_width(win);

	if(win->win_flags&WIN_PAG_VT) {
		lr=MIN_ROW+win->numr * ((win->curow-MIN_ROW)/win->numr);
		hr = (lr>MAX_ROW-win->numr) ? MAX_ROW : lr+win->numr-1;
	} else {
		rr=win->numr/2;
		lr= (win->curow<=rr) ? MIN_ROW : (win->curow-rr);
		if(lr>=MAX_ROW-win->numr) {
			hr=MAX_ROW;
			lr=hr+1-win->numr;
		} else
			hr=lr+win->numr-1;
	}
	set_numcols(win,hr);

	if(win->win_flags&WIN_PAG_HZ) {
		lc=hc=MIN_COL;
		w=get_width(hc);
		for(;;) {
			ww=get_width(hc+1);
			while(w+ww<=win->numc && hc<MAX_COL) {
				hc++;
				w+=ww;
				ww=get_width(hc+1);
			}
			if(hc>=win->cucol)
				break;
			hc++;
			lc=hc;
			w=ww;
		}
		if(lc>win->cucol || hc>MAX_COL) {
			error_msg("Can't find a non-zero-width column in recenter_window()");
			/* ... */
		}
	} else {
		lc=hc=win->cucol;
		n=totwid=get_width(win->cucol);
		do {
			if(lc>MIN_COL && totwid+(n=get_width(lc-1))<win->numc) {
				--lc;
				totwid+=n;
				more=1;
			} else
				more=0;
			if(hc<MAX_COL && totwid+(n=get_width(hc+1))<win->numc) {
				hc++;
				totwid+=n;
				more++;
			}
		} while(more);
	}
	while(get_width(lc)==0)
		lc++;
	while(get_width(hc)==0)
		--(hc);

	win->screen.lc=lc;
	win->screen.hc=hc;
	win->screen.lr=lr;
	win->screen.hr=hr;
}


int
scroll_cell_cursor FUN1(int, magic)
{
	int off_dn,off_rt;
	int n=0;
	int totwid;

	struct rng s;
	CELLREF cr,cc;
	unsigned short w;
	int over,down;

	over=colmagic[magic]*how_many;
	down=rowmagic[magic]*how_many;
	if(down) {
		off_dn=curow-cwin->screen.lr;
		n=down*cwin->numr;
		if(n>MAX_ROW-MIN_ROW) {
			error_msg(down>0 ? "Can't scroll down" : "Can't scroll up");
			return 0;
		}
		if(down>0) {
			if(cwin->screen.lr>MAX_ROW-n) {
				error_msg("Can't scroll down");
				return 0;
			}

			if(cwin->screen.hr>MAX_ROW-n) {
				s.hr=MAX_ROW;
				s.lr= (cwin->win_flags&WIN_PAG_VT) ? cwin->screen.lr+n : 1+s.hr-cwin->numr;
				cr= (off_dn>s.hr-s.lr) ? MAX_ROW : s.lr+off_dn;
			} else {
				s.lr=cwin->screen.lr+n;
				s.hr=cwin->screen.hr+n;
				cr=s.lr+off_dn;
			}
		} else {
			if(cwin->screen.hr<MIN_ROW-n) {
				error_msg("Can't scroll up");
				return 0;
			}

			if((cwin->win_flags&WIN_PAG_VT) && cwin->screen.hr==MAX_ROW) {
				s.lr=cwin->screen.lr+n;
				s.hr=s.lr+cwin->numr-1;
			} else if(cwin->screen.lr<MIN_ROW-n) {
				s.lr=MIN_ROW;
				s.hr=s.lr+cwin->numr-1;
			} else {
				s.lr=cwin->screen.lr+n;
				s.hr=cwin->screen.hr+n;
			}
			cr=s.lr+off_dn;
		}

		set_numcols(cwin,s.hr);
	} else {
		s.lr=cwin->screen.lr;
		s.hr=cwin->screen.hr;
		cr=curow;
	}

	off_rt=cucol-cwin->screen.lc;

	if(over>0) {
		if(cwin->screen.hc==MAX_COL) {
			error_msg("Can't scroll right");
			return 0;
		}

		for(s.lc=s.hc=cwin->screen.hc+1;;s.lc= ++s.hc) {
			--over;
			totwid=get_width(s.lc);
			while(s.hc<MAX_COL && totwid+(n=get_width(s.hc+1))<=cwin->numc) {
				s.hc++;
				totwid+=n;
			}
			if(!over || s.hc==MAX_COL)
				break;
		}
		if(!(cwin->win_flags&WIN_PAG_HZ)) {
			while(s.lc>MIN_COL && totwid+(n=get_width(s.lc-1))<=cwin->numc) {
				s.lc--;
				totwid+=n;
				off_rt++;
			}
		}
		cc= (s.hc-s.lc<off_rt) ? s.hc : s.lc+off_rt;
	} else if(over<0) {
		if(cwin->screen.lc==MIN_COL) {
			error_msg("Can't scroll left");
			return 0;
		}

		if(cwin->win_flags&WIN_PAG_HZ) {
			/* This is tricky! */
			int n_over;
			unsigned short ww = 0;

			n_over=0;
			for(s.lc=s.hc=MIN_COL,w=get_width(s.hc);
			    ;
			    s.lc= ++s.hc,w=ww,n_over++) {
				while(s.hc<MAX_COL && w+(ww=get_width(s.hc+1))<=cwin->numc) {
					s.hc++;
					w+=ww;
				}
				if(s.hc>=cucol)
					break;
			}
			n_over+=over;
			if(n_over<0) {
				error_msg("Can't scroll left");
				return 0;
			}

			for(s.lc=s.hc=MIN_COL,w=get_width(s.hc);
			    ;
			    s.lc=s.hc+1,s.hc++,w=ww) {
				ww=get_width(s.hc+1);
				while(w+ww<=cwin->numc && s.hc<MAX_COL) {
					s.hc++;
					w+=ww;
					ww=get_width(s.hc+1);
				}
				if(!n_over--)
					break;
			}
			over=0;
			cc= (s.hc-s.lc<off_rt) ? s.hc : s.lc+off_rt;
		} else {
			for(s.lc=s.hc=cwin->screen.lc-1;;s.hc= --s.lc) {
				over++;
				totwid=get_width(s.lc);
				while(s.lc>MIN_COL && totwid+(n=get_width(s.lc-1))<=cwin->numc) {
					s.lc--;
					totwid+=n;
				}
				if(!over || s.lc==MIN_COL)
					break;
			}
			if(s.lc==MIN_COL) {
				while(s.hc<MAX_COL && totwid+(n=get_width(s.hc+1))<=cwin->numc) {
					s.hc++;
					totwid+=n;
				}
			}
			cc= (s.hc-s.lc<off_rt) ? s.lc : s.lc+off_rt;
		}
	} else if(!(cwin->win_flags&WIN_PAG_HZ)) {
		/* If page_scroll, we don't have to bother with this.
		   It'll fit.  That's all. . . */
		totwid=0;
		s.lc=cwin->screen.lc;
		s.hc=cwin->screen.hc;
		cc=cucol;

		/* This is tricky:  ;n<=s.hc;  won't work if s.hc==MAX_CELLREF */
		for(n=s.lc;;n++) {
			totwid+=get_width(n);
			if(n==s.hc)
				break;
		}
		while(cwin->numc<totwid) {
			if(cucol==s.lc)
				totwid-=get_width(s.hc--);
			else
				totwid-=get_width(s.lc++);
		}
		while(s.hc<MAX_COL && totwid+(n=get_width(s.hc+1))<=cwin->numc) {
			++s.hc;
			totwid+=n;
		}
		while(s.lc>MIN_COL && totwid+(n=get_width(s.lc-1))<=cwin->numc) {
			--s.lc;
			totwid+=n;
		}
	} else {
		cwin->screen.lr=s.lr;
		cwin->screen.hr=s.hr;
		if(cwin->win_linked!=-1)
			shift_linked_window((long)cr-curow,(long)0);
		curow=cr;
		disp_scrn();
		return 1;
	}

	if(over) {
		error_msg(over>0 ? "Can't scroll right" : "Can't scroll left");
		return 0;
	}

	while(get_width(s.lc)==0)
		s.lc++;
	while(get_width(s.hc)==0)
		--(s.hc);

	if(cwin->win_linked!=-1)
		shift_linked_window((long)cr-curow,(long)cc-cucol);

	cwin->screen=s;
	curow=cr;
	cucol=cc;

	if(get_width(cc)==0)
		find_nonzero_width(cwin);

	disp_scrn();
	return 1;
}

void
read_mp_windows FUN1(char *,line)
{
	int wnum=0;
	char *text;
	CELLREF nrow=NON_ROW,ncol=NON_COL;
	char *split=0;
	char *opts=0;
	struct window *win;

	text=line;
	for(;;) {
		switch(*text++) {
				/* Window Number */
		case 'N':
			wnum=astol(&text);
			break;
				/* Cursor At */
		case 'A':
			nrow=astol(&text);
			ncol=astol(&text);
			break;
				/* JF: Window options */
		case 'O':
			opts=text;
			while(*text && *text!=';')
				text++;
			break;
				/* Split into two windows */
		case 'S':
			split=text;
			while(*text && *text!=';')
				text++;
			break;
				/* Set Colors NOT supported */
		case 'C':
			while(*text && *text!=';')
				text++;
			break;
				/* Alternate border NOT supported. . . */
		case 'B':
			break;
		default:
			--text;
			break;
		}
		if(*text=='\0' || *text=='\n')
			break;
		if(*text!=';') {
			char *bad;

			bad=text;
			while(*text && *text!=';')
				text++;
			if(*text)
				*text++='\0';
			error_msg("Unknown SYLK window cmd: %s",bad);
			if(!*text)
				break;
		} else
			*text++='\0';
	}
	if(wnum<1 || wnum>nwin) {
		error_msg("Window %d out of range in SYLK line %s",wnum,line);
		return;
	}
	--wnum;
	win= &wins[wnum];
	if(nrow!=NON_ROW) {
		win->curow=nrow;
		win->cucol=ncol;
		if(win==cwin) {
			curow=nrow;
			cucol=ncol;
		}
		recenter_window(win);
	}
	if(split) {
		int hv = 0;
		int where;
		int link;
		struct window *new;

		switch(*split++) {
		case 'H':
		case 'h':
			hv=1;
			break;
		case 'v':
		case 'V':
			hv=0;
			break;
		case 't':
		case 'T':
			error_msg("Window split titles not supported");
			return;
		default:
			break;
		}
		if(*split=='L') {
			link=wnum;
			split++;
		} else
			link= -1;

		where=astol(&split);

		if(hv ? where>=win->numr : where>=win->numc)
			error_msg("Can't split window: screen too small");

		nwin++;

		wins=ck_realloc(wins,nwin*sizeof(struct window));
		cwin=wins;
		win= &wins[wnum];
		new= &wins[nwin-1];

		win->numc-=(hv ? 0 : where);
		win->numr-=(hv ? where : 0);
		win->curow=curow;
		win->cucol=cucol;

		new->win_flags = WIN_EDGES|WIN_EDGE_REV;/* Mplan defaults */
		new->lh_wid=0;	/* For now */
		new->win_linked=link;

		new->win_over=win->win_over + (hv ?  -win->lh_wid : win->numc);
		new->win_down=win->win_down + (hv ? win->numr+1 : 0);
		new->numc=(hv ? win->numc+win->lh_wid : where);
		new->numr=(hv ? where-1 : win->numr);
		new->curow=curow;
		new->cucol=cucol;
		set_numcols(new,curow);
		recenter_window(win);
		recenter_window(new);
	}
	if(opts) {
		char *np;

		while(np=index(opts,',')) {
			*np='\0';
			set_options(opts);
			*np++=';';
			opts=np;
		}
		if(np=rindex(opts,'\n'))
			*np='\0';
		set_options(opts);
	}
}

void
write_mp_windows FUN1(FILE *,fp)
{
	int n;
	char buf[90];

	cwin->curow=curow;
	cwin->cucol=cucol;
	fprintf(fp,"O;status %d\n",status);
	if(nwin>1) {
		/* ... */
	}
	for(n=0;n<nwin;n++) {
		buf[0]='\0';
		if(wins[n].win_flags&WIN_LCK_HZ)
			strcat(buf,",lockh");
		if(wins[n].win_flags&WIN_LCK_VT)
			strcat(buf,",lockv");
		if(wins[n].win_flags&WIN_PAG_HZ)
			strcat(buf,",pageh");
		if(wins[n].win_flags&WIN_PAG_VT)
			strcat(buf,",pagev");
		if(wins[n].win_flags&WIN_EDGE_REV)
			strcat(buf,",standout");
		if((wins[n].win_flags&WIN_EDGES)==0)
			strcat(buf,",noedges");
		fprintf(fp,"W;N%d;A%u %u;C%d %d %d;O%s\n",n+1,wins[n].curow,wins[n].cucol,7,0,7,buf+1);
	}
}
