/* Win.c */

#include "Sys.h"
#include "Curses.h"

#include <errno.h>
#include <signal.h>

#include "Util.h"
#include "Main.h"
#include "Version.h"
#include "Recent.h"
#include "RCmd.h"
#include "LGets.h"
#include "GetPass.h"


#ifdef USE_CURSES

#include "WGets.h"

extern int endwin(void);
WINDOW *gListWin;
WINDOW *gInputWin;
WINDOW *gPromptWin;
WINDOW *gBarWin;
int gCurRow, gLastRow, gSkipToEnd;
int gPageNum;
int gMultiLineMode = 0;
int gPendingNL = 0;
int gBackgroundProcessing = 0;

int gRefresh[4];

/* Other protos, whose parameters aren't WINDOW *ptrs, are in Win.h */
void WAddCenteredStr(WINDOW *, int, char *);
void MainScrWNOutRefresh(WINDOW *w);
void WAttr(WINDOW *w, int attr, int on);

extern WINDOW *gPrefsWin, *gHostWin;

#endif	/* USE_CURSES */

string gPrompt = kLineModePrompt;
int gWinInit = 0;
char *gSprintfBuf = NULL;

extern longstring gRemoteCWD;
extern longstring gLockFileName;
extern int gConnected, gIsFromTTY, gVerbosity;
extern RemoteSiteInfo gRmtInfo;
extern string gHost;
extern int gStdout, gRealStdout, gOtherSessionRunning;
extern FILE *gTraceLogFile;
extern int gDebug, gTrace;
extern LineList gCmdHistory;


void EndWin(void)
{
	if (gWinInit) {
		gWinInit = 0;
#ifdef USE_CURSES
		clear();
		refresh();
		endwin();
#endif	/* USE_CURSES */
	}
}	/* EndWin */




void Exit(int exitStatus)
{
	if (gOtherSessionRunning == 0) {
		(void) UNLINK(gLockFileName);
	}
	EndWin();
	exit(exitStatus);
}	/* Exit */



/* The save/restore screen stuff doesn't work yet. :-(  */

void SaveScreen(void)
{
#ifdef USE_CURSES
	if (gWinInit) {
#if 0
		savetty();
		reset_shell_mode();
#endif
#if 0
		savetty();        /* save current setting */
		resetterm();      /* Set tty modes to "out of curses" state */
#endif
 		endwin();
		fflush(stdout);
		fflush(stderr);
	}
#endif	/* USE_CURSES */
}	/* SaveScreen */



static void TTYWaitForReturn(void)
{
	int tty;
	int junk;

	tty = open("/dev/tty", O_RDWR);
	if (tty != -1) {
		write(tty, "[Hit return]", 12);
		read(tty, &junk, 1);
		close(tty);
	}
}	/* TTYWaitForReturn */



void RestoreScreen(int pressKey)
{
#ifdef USE_CURSES
	if (gWinInit) {
		if (pressKey) {
			TTYWaitForReturn();
			clearok(curscr, FALSE);
			touchwin(curscr);
			wnoutrefresh(curscr);
			doupdate();
		}
		UpdateScreen(2);
	}
#endif	/* USE_CURSES */
}	/* RestoreScreen */



void Beep(int on)
{
	static time_t lastBeepTime = 0;
	time_t now;

	time(&now);

	/* Don't flood the user with beeps. Once per two seconds is reasonable. */
	if ((on > 0) && ((int) (now - lastBeepTime) > 1)) {
#ifdef USE_CURSES
		if (gWinInit)
			beep();
		else
#endif
		{
			fprintf(stderr, "\007");	/* ^G */
			fflush(stderr);
		}
	}
	lastBeepTime = now;
}	/* Beep */




#ifdef USE_CURSES
/* Many old curses libraries don't support wattron() and its attributes.
 * They should support wstandout() though.  This routine is an attempt
 * to use the best substitute available, depending on what the curses
 * library has.
 */
void WAttr(WINDOW *w, int attr, int on)
{
	/* Define PLAIN_TEXT_ONLY if you have the attributes, but don't want
	 * to use them.
	 */
#ifndef PLAIN_TEXT_ONLY
#ifdef A_REVERSE
	if (attr & kReverse) {
		if (on)
			wattron(w, A_REVERSE);
		else
			wattroff(w, A_REVERSE);
	}
#else
	if (attr & kReverse) {
		if (on)
			wstandout(w);
		else
			wstandend(w);

		/* Nothing else will be done anyway, so just return now. */
		return;
	}
#endif	/* A_REVERSE */

#ifdef A_BOLD
	if (attr & kBold) {
		if (on)
			wattron(w, A_BOLD);
		else
			wattroff(w, A_BOLD);
	}
#else
	/* Do nothing.  Plain is best substitute. */
#endif	/* A_BOLD */

#ifdef A_UNDERLINE
	if (attr & kUnderline) {
		if (on)
			wattron(w, A_UNDERLINE);
		else
			wattroff(w, A_UNDERLINE);
	}
#else
	/* Try using standout mode in place of underline. */
	if (attr & kUnderline) {
		if (on)
			wstandout(w);
		else
			wstandend(w);

		/* Nothing else will be done anyway, so just return now. */
		return;
	}
#endif	/* A_UNDERLINE */

#ifdef A_DIM
	if (attr & kDim) {
		if (on)
			wattron(w, A_DIM);
		else
			wattroff(w, A_DIM);
	}
#else
	/* Do nothing.  Plain is best substitute. */
#endif	/* A_DIM */

#ifdef A_NORMAL
	if (attr == kNormal) {
		wattrset(w, A_NORMAL);
		return;
	}
#else
	/* At least make sure standout mode is off. */
	if (attr == kNormal) {
		wstandend(w);
		return;
	}
#endif	/* A_NORMAL */
#endif	/* PLAIN_TEXT_ONLY */
}	/* WAttr */
#endif	/* USE_CURSES */




#ifdef USE_CURSES
/* Curses keeps doing unnecessary screen updates.  I hope this helps
 * get around that.
 */
void MainScrWNOutRefresh(WINDOW *w)
{
	int i;

	if (w == gListWin)
		i = kListWinIndex;
	else if (w == gInputWin)
		i = kInputWinIndex;
	else if (w == gPromptWin)
		i = kPromptWinIndex;
	else if (w == gBarWin)
		i = kBarWinIndex;
	else
		return;
	
	/* We'll keep a flag for each window whether we are supposed to
	 * do an update when we get back to Gets().  Then when we do
	 * arrive there, we'll do one doupdate() only and draw everything
	 * only once.
	 */
	if (gRefresh[i] == 0) {
		gRefresh[i] = 1;
	}
}	/* MainScrWNOutRefresh */
#endif	/* USE_CURSES */




#ifdef USE_CURSES
void MainScrDoUpdate(void)
{
	int i;
	int doUp;
	
	doUp = 0;

	i = kListWinIndex;
	if (gRefresh[i]) {
		wnoutrefresh(gListWin);
		doUp = 1;
		gRefresh[i] = 0;
	}

	i = kPromptWinIndex;
	if (gRefresh[i]) {
		wnoutrefresh(gPromptWin);
		doUp = 1;
		gRefresh[i] = 0;
	}

	i = kBarWinIndex;
	if (gRefresh[i]) {
		wnoutrefresh(gBarWin);
		doUp = 1;
		gRefresh[i] = 0;
	}

	i = kInputWinIndex;
	if (gRefresh[i]) {
		wnoutrefresh(gInputWin);
		doUp = 1;
		gRefresh[i] = 0;
	}

	if (doUp > 0)
		doupdate();
}	/* MainScrDoUpdate */
#endif	/* USE_CURSES */




void UpdateScreen(int wholeScreen)
{
#ifdef USE_CURSES
	int i;

	if (gWinInit) {
		if (wholeScreen) {

			/* Reset the refresh tracking. */
			for (i = kListWinIndex; i <= kBarWinIndex; i++)
				gRefresh[i] = 0;

			touchwin(gListWin);
			touchwin(gPromptWin);
			touchwin(gInputWin);
			touchwin(gBarWin);
				
			MainScrWNOutRefresh(gListWin);
			MainScrWNOutRefresh(gPromptWin);
			MainScrWNOutRefresh(gInputWin);
			MainScrWNOutRefresh(gBarWin);

			if (wholeScreen == 1) {
				MainScrDoUpdate();

				/* Double update :-( */
				touchwin(curscr);
				wnoutrefresh(curscr);
				doupdate();
			} else if (wholeScreen == 2) {
				touchwin(curscr);
				wnoutrefresh(curscr);
				MainScrDoUpdate();
			}
		} else {
			MainScrDoUpdate();
		}
	}
#endif	/* USE_CURSES */
}	/* UpdateScreen */



void FlushListWindow(void)
{
#ifdef USE_CURSES
	if (gWinInit) {
		MainScrWNOutRefresh(gListWin);
		MainScrDoUpdate();
		return;
	}
#endif	/* USE_CURSES */
	fflush(stdout);
	fflush(stderr);		/* Overkill, since stderr _should_ be unbuffered. */
}	/* FlushListWindow */




#ifdef USE_CURSES
#ifdef SIGTSTP
static
void SigTStp(int sigNum)
{
/* TO-DO: (doesn't work 100% correctly yet) */
	if (sigNum == SIGTSTP) {
		if ((gPrefsWin != NULL) || (gHostWin != NULL)) {
			/* Doing this with these windows open can cause problems. */
			return;
		}
		SIGNAL(SIGTSTP, SIG_DFL);
		SaveScreen();
		TraceMsg("\nSuspended.\n");
		kill(getpid(), SIGSTOP);	/* Send stop signal to ourselves. */
	} else {
		SIGNAL(SIGTSTP, SigTStp);
		SIGNAL(SIGCONT, SigTStp);
		TraceMsg("\nResumed.\n");
		sleep(1);
		if (InForeGround())
			RestoreScreen(0);
		else
			gBackgroundProcessing = 1;
	}
}	/* SigTStp */
#endif
#endif	/* USE_CURSES */





/* Read a line of input, and axe the end-of-line. */
char *Gets(char *str, size_t size)
{
	string pr;
#ifdef USE_CURSES
	int result;
	WGetsParams wgp;
	int maxy, maxx;
#endif	/* USE_CURSES */	

#ifdef USE_CURSES
	if (gWinInit) {
		if (gBackgroundProcessing) {
			while (! InForeGround())
				sleep(1);
			gBackgroundProcessing = 0;
			RestoreScreen(0);
		}

		str[0] = '\0';
		/* if (!gPendingNL) */
			MainScrWNOutRefresh(gListWin);
		if (gMultiLineMode) {
			SetPrompt(gPrompt);
			gMultiLineMode = 0;
		} else {
			werase(gInputWin);
			MainScrWNOutRefresh(gInputWin);
		}
		MainScrDoUpdate();
#ifdef HAVE_LIBREADLINE_DOES_NOT_WORK_YET
		{
			char *cp;
			string prompt;

			wmove(gInputWin, 0, 0);
		/*	MainScrWNOutRefresh(gInputWin); */
		/*	MainScrDoUpdate(); */
		/*	reset_shell_mode(); */
			sprintf(prompt, "%s> ", gPrompt);
			SaveScreen();
		/*	cbreak(); */
			cp = ReadlineGets(prompt, str, size);
		/*	nocbreak(); */
		/*	reset_prog_mode(); */
		/*	waddstr(gInputWin, cp); */
			werase(gInputWin);
			wrefresh(gInputWin);
			MainScrWNOutRefresh(gInputWin);
			return cp;
		}
#endif
		wgp.w = gInputWin;
		wgp.sy = 0;
		wgp.sx = 0;

		getmaxyx(gInputWin, maxy, maxx);
		wgp.fieldLen = maxx - 1;
		wgp.dst = str;
		wgp.dstSize = size;
		wgp.useCurrentContents = 0;
		wgp.echoMode = wg_RegularEcho;
		wgp.history = &gCmdHistory;
		result = wg_Gets(&wgp);
		if (result < 0)
			return (NULL);	/* Error, or EOF. */
		return (str);
	} else
#endif	/* USE_CURSES */
	{
		Echo(stdin, 1);		/* Turn echo on, if it wasn't already. */
		STRNCPY(pr, gPrompt);
		STRNCAT(pr, kPromptTail);
		return LineModeGets(pr, str, size);
	}
}	/* Gets */




void GetAnswer(char *prompt, char *answer, size_t siz, int noEcho)
{
#ifdef USE_CURSES
	WGetsParams wgp;
	int maxy, maxx;
#endif

	PTRZERO(answer, siz);
#ifdef USE_CURSES
	if (gWinInit) {
		MainScrWNOutRefresh(gListWin);
		MakeBottomLine(prompt, kReverse, 0);
		MainScrDoUpdate();
		wgp.w = gInputWin;
		wgp.sy = 0;
		wgp.sx = 0;
		getmaxyx(gInputWin, maxy, maxx);
		wgp.fieldLen = maxx - 1;
		wgp.dst = answer;
		wgp.dstSize = siz;
		wgp.useCurrentContents = 0;
		wgp.echoMode = noEcho ? wg_BulletEcho : wg_RegularEcho;
		wgp.history = wg_NoHistory;
		wg_Gets(&wgp);
		return;
	}
#endif	/* USE_CURSES */
	if (noEcho)
		GetPass(prompt, answer, siz);
	else {
		StdioGets(prompt, answer, siz);
	}
}	/* GetAnswer */




void SetBarLeftRight(char *l, char *r, int prefSide)
{
#ifdef USE_CURSES
	int llen, rlen;
	int maxy, maxx;
	int i;

	if (gWinInit) {
		llen = (int) strlen(l);
		rlen = (int) strlen(r);
		
		getmaxyx(gBarWin, maxy, maxx);
		if (llen + rlen > maxx - 1)
			SetBarCentered(prefSide == 'R' ? r : l);
		
		wmove(gBarWin, 0, 0);
		waddstr(gBarWin, l);
		
		i = maxx - llen - rlen;
		while (--i >= 0)
			waddch(gBarWin, ' ');
		waddstr(gBarWin, r);
		MainScrWNOutRefresh(gBarWin);
	}
#endif	/* USE_CURSES */
}	/* SetBarLeftRight */




void SetBarCentered(char *s)
{
#ifdef USE_CURSES
	int len;
	int i, j;
	int maxy, maxx;

	if (gWinInit) {
		len = (int) strlen(s);
		
		wmove(gBarWin, 0, 0);
		getmaxyx(gBarWin, maxy, maxx);
		i = (maxx - len) / 2;
		if (i < 0)
			i = 0;
		j = i;
		while (--j >= 0)
			waddch(gBarWin, ' ');
		waddstr(gBarWin, s);
		for (i += len; i < maxx; ++i)
			waddch(gBarWin, ' ');
		MainScrWNOutRefresh(gBarWin);
	}
#endif	/* USE_CURSES */
}	/* SetBarCentered */




void SetDefaultBar(void)
{
	string str;

	if (gWinInit) {
		STRNCPY(str, "NcFTP ");
		STRNCAT(str, kVersion);
		STRNCAT(str, " by Mike Gleason, NCEMRSoft (mgleason@cse.unl.edu).");
		SetBarCentered(str);
	} else {
		SetPrompt(kUseDefaultPrompt);
	}
}	/* SetDefaultBar */




void SetScreenInfo(void)
{
	string pr;
	char *cp;
	int len, len2;

	if (gWinInit) {
		if (gConnected) {
			SetBarLeftRight(gRmtInfo.name, gRemoteCWD, 'R');
			SetPrompt(gRmtInfo.nickName);
		} else {
			SetDefaultBar();
			SetPrompt(kUseDefaultPrompt);
		}
	} else {
		if (gConnected) {
			STRNCPY(pr, gRmtInfo.nickName);
			STRNCAT(pr, ":");
			len = (int) strlen(pr);
			len2 = (int) strlen(gRemoteCWD);
			if (len + len2 > kPromptLimit) {
				STRNCAT(pr, "...");
				cp = gRemoteCWD + len2 - (kPromptLimit - len - 3);
				STRNCAT(pr, cp);
			} else {
				STRNCAT(pr, gRemoteCWD);
			}
			SetPrompt(pr);
		} else {
			SetPrompt(kUseDefaultPrompt);
		}
	}
}	/* SetScreenInfo */




#ifdef USE_CURSES
void PrintToListWindow(char *buf, int multi)
{
	char *endp, *startp;
	int c, haveNL;
	int haveCR;
	int y, x;
	string pr;

	if ((multi) && (gSkipToEnd == 1))
		return;

	endp = buf;
	startp = buf;

	while (1) {
		if (*startp == '\0')
			break;
		haveCR = 0;
		haveNL = 0;
		for (endp = startp; ; endp++) {
			if (*endp == '\0') {
				endp = NULL;
				break;
			} else if (*endp == '\n') {
				haveNL = 1;
				*endp = '\0';
				break;
			} else if (*endp == '\r') {
				/* Have to do CRs manually because
				 * some systems don't do 'em.  (AIX).
				 */
				haveCR = 1;
				*endp = '\0';
				break;
			}
		}
		if ((multi) && (gCurRow >= gLastRow) && (!gSkipToEnd) && (!haveCR)) {
			MainScrWNOutRefresh(gListWin);
			sprintf(pr, "--Page %d--", (++gPageNum));
			MakeBottomLine(pr, kReverse, 0);
			MainScrDoUpdate();
			cbreak();
			c = mvwgetch(gInputWin, 0, 0);
			nocbreak();
			if (c == 'q') {
				gSkipToEnd = 1;
				gCurRow = 0;
				gPendingNL = 1;
				break;
			}
			gCurRow = 0;
		}
		
		if (gPendingNL) {
			waddch(gListWin, '\n');
		}

		/* Weird things happened if I did wprintw(gListWin, "%s", startp). */
		waddstr(gListWin, startp);

		/* May need to gCurRow++ if line wraps around other side... */
		if (haveNL) {
			gCurRow++;
		} else if (haveCR) {
			getyx(gListWin, y, x);
			wmove(gListWin, y, 0);
		}
		gPendingNL = haveNL;
		if (endp == NULL)
			break;
		startp = endp + 1;
	}
}	/* PrintToListWindow */
#endif	/* USE_CURSES */




/* Prints a message, if you have debbuging mode turned on. */

/*VARARGS*/
#ifndef HAVE_STDARG_H
void DebugMsg(va_alist)
	va_dcl
#else
void DebugMsg(char *fmt0, ...)
#endif
{
	va_list ap;
	char *fmt;

#ifndef HAVE_STDARG_H
	va_start(ap);
	fmt = va_arg(ap, char *);
#else
	va_start(ap, fmt0);
	fmt = fmt0;
#endif

	if (gDebug == kDebuggingOn) {
		if (gWinInit) {
#ifdef USE_CURSES
			strcpy(gSprintfBuf, "#DB# ");
			(void) vsprintf(gSprintfBuf + 5, fmt, ap);
			PrintToListWindow(gSprintfBuf, 0);
#endif	/* USE_CURSES */
		} else {
			(void) fprintf(kDebugStream, "#DB# ");
			(void) vfprintf(kDebugStream, fmt, ap);
			(void) fflush(kDebugStream);
		}
	}
	if ((gTrace == kTracingOn) && (gTraceLogFile != NULL)) {
		(void) fprintf(gTraceLogFile, "#DB# ");
		(void) vfprintf(gTraceLogFile, fmt, ap);
		(void) fflush(gTraceLogFile);
	}
	va_end(ap);
}	/* DebugMsg */




/* This is similar to DebugMsg, but only writes to the debug log
 * file.  This is useful for putting messages in the log that
 * shouldn't show up on screen (i.e. would make a mess in visual
 * mode.
 */

/*VARARGS*/
#ifndef HAVE_STDARG_H
void TraceMsg(va_alist)
	va_dcl
#else
void TraceMsg(char *fmt0, ...)
#endif
{
	va_list ap;
	char *fmt;

#ifndef HAVE_STDARG_H
	va_start(ap);
	fmt = va_arg(ap, char *);
#else
	va_start(ap, fmt0);
	fmt = fmt0;
#endif

	if ((gTrace == kTracingOn) && (gTraceLogFile != NULL)) {
		(void) fprintf(gTraceLogFile, "#TR# ");
		(void) vfprintf(gTraceLogFile, fmt, ap);
		(void) fflush(gTraceLogFile);
	}
	va_end(ap);
}	/* TraceMsg */




/* Prints to our own standard output stream. */

/*VARARGS*/
#ifndef HAVE_STDARG_H
void PrintF(va_alist)
	va_dcl
#else
void PrintF(char *fmt0, ...)
#endif
{
	va_list ap;
	char *fmt;

#ifndef HAVE_STDARG_H
	va_start(ap);
	fmt = va_arg(ap, char *);
#else
	va_start(ap, fmt0);
	fmt = fmt0;
#endif

	/* If it's an important message, don't use this function, use
	 * EPrintF() instead.
	 */
	if (gVerbosity > kErrorsOnly) {
		if ((gWinInit) && (gRealStdout == gStdout)) {
#ifdef USE_CURSES
			(void) vsprintf(gSprintfBuf, fmt, ap);
			PrintToListWindow(gSprintfBuf, 0);
#endif	/* USE_CURSES */
		} else {
			(void) vsprintf(gSprintfBuf, fmt, ap);
			(void) write(gStdout, gSprintfBuf, strlen(gSprintfBuf));
		}
	}
	if ((gTrace == kTracingOn) && (gTraceLogFile != NULL)) {
		(void) vfprintf(gTraceLogFile, fmt, ap);
		(void) fflush(gTraceLogFile);
	}
	va_end(ap);
}	/* PrintF */



/*VARARGS*/
#ifndef HAVE_STDARG_H
void BoldPrintF(va_alist)
	va_dcl
#else
void BoldPrintF(char *fmt0, ...)
#endif
{
	va_list ap;
	char *fmt;

#ifndef HAVE_STDARG_H
	va_start(ap);
	fmt = va_arg(ap, char *);
#else
	va_start(ap, fmt0);
	fmt = fmt0;
#endif

	if (gVerbosity > kErrorsOnly) {
		if ((gWinInit) && (gRealStdout == gStdout)) {
#ifdef USE_CURSES
			(void) vsprintf(gSprintfBuf, fmt, ap);
			WAttr(gListWin, kBold, 1);
			PrintToListWindow(gSprintfBuf, 0);
			WAttr(gListWin, kBold, 0);
#endif	/* USE_CURSES */
		} else {
			(void) vsprintf(gSprintfBuf, fmt, ap);
			(void) write(gStdout, gSprintfBuf, strlen(gSprintfBuf));
		}
	}
	if ((gTrace == kTracingOn) && (gTraceLogFile != NULL)) {
		(void) vfprintf(gTraceLogFile, fmt, ap);
		(void) fflush(gTraceLogFile);
	}
	va_end(ap);
}	/* BoldPrintF */




/* Prints to stderr. */

/*VARARGS*/
#ifndef HAVE_STDARG_H
void EPrintF(va_alist)
	va_dcl
#else
void EPrintF(char *fmt0, ...)
#endif
{
	va_list ap;
	char *fmt;
	char *cp;

#ifndef HAVE_STDARG_H
	va_start(ap);
	fmt = va_arg(ap, char *);
#else
	va_start(ap, fmt0);
	fmt = fmt0;
#endif

	if (gVerbosity > kQuiet) {
		if (gWinInit) {
#ifdef USE_CURSES
			(void) vsprintf(gSprintfBuf, fmt, ap);
			PrintToListWindow(gSprintfBuf, 0);
			
			/* No buffering on error stream. */
			MainScrWNOutRefresh(gListWin);
			MainScrDoUpdate();
#endif	/* USE_CURSES */
		} else {
			(void) vfprintf(stderr, fmt, ap);
		}
	}
	if ((gTrace == kTracingOn) && (gTraceLogFile != NULL)) {
		/* Special hack so when progress meters use \r's we don't
		 * print them in the trace file.
		 */
		(void) vsprintf(gSprintfBuf, fmt, ap);
		for (cp = gSprintfBuf; ; ) {
			/* Replace all carriage returns with newlines. */
			cp = strchr(cp, '\r');
			if (cp == NULL)
				break;
			*cp++ = '\n';
		}
		fputs(gSprintfBuf, gTraceLogFile);
		(void) fflush(gTraceLogFile);
	}
	va_end(ap);
}	/* EPrintF */





/*VARARGS*/
#ifndef HAVE_STDARG_H
void Error(va_alist)
	va_dcl
#else
void Error(int pError0, char *fmt0, ...)
#endif
{
	va_list ap;
	char *fmt;
	int pError;
	longstring buf2;
	
#ifndef HAVE_STDARG_H
	va_start(ap);
	pError = va_arg(ap, int);
	fmt = va_arg(ap, char *);
#else
	va_start(ap, fmt0);
	fmt = fmt0;
	pError = pError0;
#endif

	if (gVerbosity > kQuiet) {
		if (gWinInit) {
#ifdef USE_CURSES
			(void) vsprintf(gSprintfBuf, fmt, ap);
			if (gDebug == kDebuggingOn)
				sprintf(buf2, "Error(%d): ", errno);
			else
				STRNCPY(buf2, "Error: ");
			STRNCAT(buf2, gSprintfBuf);
#ifdef HAVE_STRERROR
			if ((pError == kDoPerror) && (errno > 0)) {
				STRNCAT(buf2, "Reason: ");
				STRNCAT(buf2, strerror(errno));
				STRNCAT(buf2, "\n");
			}
#endif	/* HAVE_STRERROR */
			PrintToListWindow(buf2, 0);
#endif	/* USE_CURSES */
		} else {
			(void) fprintf(stderr, "Error");
			if (gDebug == kDebuggingOn)
				(void) fprintf(stderr, "(%d)", errno);
			(void) fprintf(stderr, ": ");
			(void) vfprintf(stderr, fmt, ap);
			(void) fflush(stderr);
			if ((pError == kDoPerror) && (errno > 0))
				perror("Reason");
		}
	}
	if ((gTrace == kTracingOn) && (gTraceLogFile != NULL)) {
		(void) fprintf(gTraceLogFile, "Error(%d): ", errno);
		(void) vfprintf(gTraceLogFile, fmt, ap);
#ifdef HAVE_STRERROR
		if ((pError == kDoPerror) && (errno > 0))
			(void) fprintf(gTraceLogFile, "Reason: %s\n", strerror(errno));
#endif
		(void) fflush(gTraceLogFile);
	}
	va_end(ap);
}	/* Error */




void MultiLineInit(void)
{
#ifdef USE_CURSES
	int maxy, maxx;

	if (gWinInit) {
		gCurRow = 0;
		getmaxyx(gListWin, maxy, maxx);
		gLastRow = maxy;
		gSkipToEnd = 0;
		gPageNum = 0;
		gMultiLineMode = 1;
	}
#endif	/* USE_CURSES */
}	/* MultiLineInit */



/*VARARGS*/
#ifndef HAVE_STDARG_H
void MultiLinePrintF(va_alist)
	va_dcl
#else
void MultiLinePrintF(char *fmt0, ...)
#endif
{
	va_list ap;
	char *fmt;

#ifndef HAVE_STDARG_H
	va_start(ap);
	fmt = va_arg(ap, char *);
#else
	va_start(ap, fmt0);
	fmt = fmt0;
#endif

	if (gVerbosity > kErrorsOnly) {
		if ((gWinInit) && (gRealStdout == gStdout)) {
#ifdef USE_CURSES		
			(void) vsprintf(gSprintfBuf, fmt, ap);
			PrintToListWindow(gSprintfBuf, 1);
#endif	/* USE_CURSES */
		} else {
			(void) vsprintf(gSprintfBuf, fmt, ap);
			(void) write(gStdout, gSprintfBuf, strlen(gSprintfBuf));
		}
	}
	if ((gTrace == kTracingOn) && (gTraceLogFile != NULL)) {
		(void) vfprintf(gTraceLogFile, fmt, ap);
		(void) fflush(gTraceLogFile);
	}

	va_end(ap);
}	/* MultiLinePrintF */




void MakeBottomLine(char *pr, int flags, int addTail)
{
#ifdef USE_CURSES
	int len;
	int doCreate;
	int maxy, maxx;

	len = (int) strlen(pr);
	if (addTail)
		len += strlen(kPromptTail);
	else if (flags & kReverse)
		len++;	/* Will add a space so you can see cursor. */

	doCreate = 0;
	if (gPromptWin != NULL) {
		getmaxyx(gPromptWin, maxy, maxx);
		if (maxx != len) {
			delwin(gPromptWin);
			delwin(gInputWin);
			doCreate = 1;
		}
	} else {
		doCreate = 1;
	}

	if (doCreate) {
		gPromptWin = newwin(1, len, LINES - 1, 0);
		gInputWin = newwin(1, COLS - len, LINES - 1, len);

		if ((gPromptWin == NULL) || (gInputWin == NULL))
			Exit(kExitWinFail2);
	}

	WAttr(gPromptWin, flags, 1);
	if (addTail) {
		mvwprintw(gPromptWin, 0,0, "%s%s", pr, kPromptTail);
		WAttr(gPromptWin, flags, 0);
	} else {
		mvwprintw(gPromptWin, 0,0, "%s", pr);
		WAttr(gPromptWin, flags, 0);
		if (flags & kReverse)
			wprintw(gPromptWin, " ");
	}
	MainScrWNOutRefresh(gPromptWin);
	werase(gInputWin);
	touchwin(gInputWin);
	MainScrWNOutRefresh(gInputWin);
	MainScrDoUpdate();
#endif	/* USE_CURSES */
}	/* MakeBottomLine */




void SetPrompt(char *pr)
{
	string p;

	if ((pr == kUseDefaultPrompt)
		|| STREQ(pr, kLineModePrompt)
		|| STREQ(pr, kVisualModePrompt)) {
		if (gWinInit)
			STRNCPY(p, kVisualModePrompt);
		else
			STRNCPY(p, kLineModePrompt);
	} else {
		STRNCPY(p, pr);
	}
	STRNCPY(gPrompt, p);
#ifdef USE_CURSES
	if (gWinInit)
		MakeBottomLine(p, kBold, 1);
#endif	/* USE_CURSES */
}	/* SetPrompt */



#ifdef USE_CURSES
/* Draws a string centered in a window. */
void WAddCenteredStr(WINDOW *w, int y, char *str)
{
	int x;
	int maxy, maxx;

	getmaxyx(w, maxy, maxx);
	x = (maxx - strlen(str)) / 2;
	if (x < 0)
		x = 0;
	wmove(w, y, x);
	waddstr(w, str);
}	/* WAddCenteredStr */
#endif	/* USE_CURSES */




void InitWindows(int on)
{
#ifdef USE_CURSES
	int i;

	if (on) {
		initscr();
		if (stdscr == NULL)
			goto fail;
		gWinInit = 1;

		nl();

		gPromptWin = gInputWin = NULL;
		gListWin = newwin(LINES - 2, COLS, 0, 0);
		gBarWin = newwin(1, COLS, LINES - 2, 0);

		if ((gListWin == NULL) || (gBarWin == NULL))
			Exit(kExitWinFail1);

		scrollok(gListWin, TRUE);
		idlok(gListWin, TRUE);
		wmove(gListWin, 0, 0);
		noecho();	/* Leave this off until we need it. */

		WAttr(gBarWin, kReverse, 1);

		/* Init the refresh tracking. */
		for (i = kListWinIndex; i <= kBarWinIndex; i++)
			gRefresh[i] = 0;

		LoadHistory();

		/* Probably not set yet, unless you specified a host on
		 * the command line.
		 */
		SetScreenInfo();
#ifdef SIGTSTP
		if (SIGNAL(SIGTSTP, SIG_IGN) != SIG_IGN) {
			SIGNAL(SIGTSTP, SigTStp);
			SIGNAL(SIGCONT, SigTStp);
		}
#endif
		if ((gSprintfBuf = malloc(SZ(4096))) == NULL)
			OutOfMemory();
		return;
	}
#endif	/* USE_CURSES */
fail:
	gWinInit = 0;

	/* Prompt will already be set if connected. */
	if (gConnected == 0)
		SetPrompt(kUseDefaultPrompt);
}	/* InitWindows */

/* eof */

