/* Copyright (c) 2008, 2009
 *      Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
 *      Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
 *      Micah Cowan (micah@cowan.name)
 *      Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
 * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007
 *      Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
 *      Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
 * Copyright (c) 1987 Oliver Laumann
 *
 * This program 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 3, or (at your option)
 * any later version.
 *  
 * This program 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 this program (see the file COPYING); if not, see
 * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 *
 ****************************************************************
 */

#include <sys/types.h>
#include <fcntl.h>
#ifndef sun	/* we want to know about TIOCPKT. */
# include <sys/ioctl.h>
#endif

#include "config.h"
#include "screen.h"
#include "braille.h"
#include "extern.h"
#include "logfile.h"

extern struct display *display, *displays;
extern struct win *fore;	/* for 83 escape */
extern struct layer *flayer;	/* for 83 escape */

extern struct NewWindow nwin_default;	/* for ResetWindow() */
extern int  nversion;		/* numerical version of screen */
extern int  log_flush, logtstamp_on, logtstamp_after;
extern char *logtstamp_string;
extern char *captionstring;
extern char *hstatusstring;
extern char *wliststr;
#ifdef COPY_PASTE
extern int compacthist;
#endif
#ifdef MULTIUSER
extern struct acluser *EffectiveAclUser;
#endif

/* widths for Z0/Z1 switching */
const int Z0width = 132;
const int Z1width = 80;

/* globals set in WriteString */
static struct win *curr;	/* window we are working on */
static int rows, cols;		/* window size of the curr window */

int visual_bell = 0;
int use_hardstatus = 1;		/* display status line in hs */
char *printcmd = 0;
int use_altscreen = 0;		/* enable alternate screen support? */

unsigned char *blank;		/* line filled with spaces */
unsigned char *null;		/* line filled with '\0' */

struct mline mline_old;
struct mline mline_blank;
struct mline mline_null;

struct mchar mchar_null;
struct mchar mchar_blank = {' ' /* , 0, 0, ... */};
struct mchar mchar_so    = {' ', A_SO /* , 0, 0, ... */};

int renditions[NUM_RENDS] = {65529 /* =ub */, 65531 /* =b */, 65533 /* =u */ };

/* keep string_t and string_t_string in sync! */
static char *string_t_string[] =
{
  "NONE",
  "DCS",			/* Device control string */
  "OSC",			/* Operating system command */
  "APC",			/* Application program command */
				/*  - used for status change */
  "PM",				/* Privacy message */
  "AKA",			/* title for current screen */
  "GM",				/* Global message to every display */
  "STATUS"			/* User hardstatus line */
};

/* keep state_t and state_t_string in sync! */
static char *state_t_string[] =
{
  "LIT",			/* Literal input */
  "ESC",			/* Start of escape sequence */
  "ASTR",			/* Start of control string */
  "STRESC",			/* ESC seen in control string */
  "CSI",			/* Reading arguments in "CSI Pn ;...*/
  "PRIN",			/* Printer mode */
  "PRINESC",			/* ESC seen in printer mode */
  "PRINCSI",			/* CSI seen in printer mode */
  "PRIN4"			/* CSI 4 seen in printer mode */
};

static int  Special __P((int));
static void DoESC __P((int, int));
static void DoCSI __P((int, int));
static void StringStart __P((enum string_t));
static void StringChar __P((int));
static int  StringEnd __P((void));
static void PrintStart __P((void));
static void PrintChar __P((int));
static void PrintFlush __P((void));
#ifdef FONT
static void DesignateCharset __P((int, int));
static void MapCharset __P((int));
static void MapCharsetR __P((int));
#endif
static void SaveCursor __P((struct cursor *));
static void RestoreCursor __P((struct cursor *));
static void BackSpace __P((void));
static void Return __P((void));
static void LineFeed __P((int));
static void ReverseLineFeed __P((void));
static void InsertChar __P((int));
static void DeleteChar __P((int));
static void DeleteLine __P((int));
static void InsertLine __P((int));
static void Scroll __P((char *, int, int, char *));
static void ForwardTab __P((void));
static void BackwardTab __P((void));
static void ClearScreen __P((void));
static void ClearFromBOS __P((void));
static void ClearToEOS __P((void));
static void ClearLineRegion __P((int, int));
static void CursorRight __P((int));
static void CursorUp __P((int));
static void CursorDown __P((int));
static void CursorLeft __P((int));
static void ASetMode __P((int));
static void SelectRendition __P((void));
static void RestorePosRendition __P((void));
static void FillWithEs __P((void));
static void FindAKA __P((void));
static void Report __P((char *, int, int));
static void ScrollRegion __P((int));
#ifdef COPY_PASTE
static void WAddLineToHist __P((struct win *, struct mline *));
#endif
static void WLogString __P((struct win *, char *, int));
static void WReverseVideo __P((struct win *, int));
static int  WindowChangedCheck __P((char *, int, int *));
static void MFixLine __P((struct win *, int, struct mchar *));
static void MScrollH __P((struct win *, int, int, int, int, int));
static void MScrollV __P((struct win *, int, int, int, int));
static void MClearArea __P((struct win *, int, int, int, int, int));
static void MInsChar __P((struct win *, struct mchar *, int, int));
static void MPutChar __P((struct win *, struct mchar *, int, int));
static void MPutStr __P((struct win *, char *, int, struct mchar *, int, int));
static void MWrapChar __P((struct win *, struct mchar *, int, int, int, int));
#ifdef COLOR
static void MBceLine __P((struct win *, int, int, int, int));
#endif

#ifdef COLOR
# define CURR_BCE (curr->w_bce ? rend_getbg(&curr->w_rend) : 0)
#else
# define CURR_BCE 0
#endif

void
ResetAnsiState(p)
struct win *p;
{
  p->w_state = LIT;
  p->w_StringType = NONE;
}

void
ResetWindow(p)
register struct win *p;
{
  register int i;

  p->w_wrap = nwin_default.wrap;
  p->w_origin = 0;
  p->w_insert = 0;
  p->w_revvid = 0;
  p->w_mouse = 0;
  p->w_curinv = 0;
  p->w_curvvis = 0;
  p->w_autolf = 0;
  p->w_keypad = 0;
  p->w_cursorkeys = 0;
  p->w_top = 0;
  p->w_bot = p->w_height - 1;
  p->w_saved.on = 0;
  p->w_x = p->w_y = 0;
  p->w_state = LIT;
  p->w_StringType = NONE;
  bzero(p->w_tabs, p->w_width);
  for (i = 8; i < p->w_width; i += 8)
    p->w_tabs[i] = 1;
  p->w_rend = mchar_null;
#ifdef FONT
  ResetCharsets(p);
#endif
#ifdef COLOR
  p->w_bce = nwin_default.bce;
#endif
}

/* adds max 22 bytes */
int
GetAnsiStatus(w, buf)
struct win *w;
char *buf;
{
  char *p = buf;

  if (w->w_state == LIT)
    return 0;

  strcpy(p, state_t_string[w->w_state]);
  p += strlen(p);
  if (w->w_intermediate)
    {
      *p++ = '-';
      if (w->w_intermediate > 0xff)
	p += AddXChar(p, w->w_intermediate >> 8);
      p += AddXChar(p, w->w_intermediate & 0xff);
      *p = 0;
    }
  if (w->w_state == ASTR || w->w_state == STRESC)
    sprintf(p, "-%s", string_t_string[w->w_StringType]);
  p += strlen(p);
  return p - buf;
}


#ifdef FONT

void
ResetCharsets(p)
struct win *p;
{
  p->w_gr = nwin_default.gr;
  p->w_c1 = nwin_default.c1;
  SetCharsets(p, "BBBB02");
  if (nwin_default.charset)
    SetCharsets(p, nwin_default.charset);
#ifdef ENCODINGS
  ResetEncoding(p);
#endif
}

void
SetCharsets(p, s)
struct win *p;
char *s;
{
  int i;

  for (i = 0; i < 4 && *s; i++, s++)
    if (*s != '.')
      p->w_charsets[i] = ((*s == 'B') ? ASCII : *s);
  if (*s && *s++ != '.')
    p->w_Charset = s[-1] - '0';
  if (*s && *s != '.')
    p->w_CharsetR = *s - '0';
  p->w_ss = 0;
  p->w_FontL = p->w_charsets[p->w_Charset];
  p->w_FontR = p->w_charsets[p->w_CharsetR];
}
#endif	/* FONT */

/*****************************************************************/


/*
 *  Here comes the vt100 emulator
 *  - writes logfiles,
 *  - sets timestamp and flags activity in window.
 *  - record program output in window scrollback
 *  - translate program output for the display and put it into the obuf.
 *
 */
void
WriteString(wp, buf, len)
struct win *wp;
register char *buf;
register int len;
{
  register int c;
#ifdef FONT
  register int font;
#endif
  struct canvas *cv;

  if (!len)
    return;
  if (wp->w_log)
    WLogString(wp, buf, len);

  /* set global variables (yuck!) */
  curr = wp;
  cols = curr->w_width;
  rows = curr->w_height;

  if (curr->w_silence)
    SetTimeout(&curr->w_silenceev, curr->w_silencewait * 1000);

  if (curr->w_monitor == MON_ON)
    {
      debug2("ACTIVITY %d %d\n", curr->w_monitor, curr->w_bell);
      curr->w_monitor = MON_FOUND;
    }

  if (cols > 0 && rows > 0)
    {
      do
	{
	  c = (unsigned char)*buf++;
#ifdef FONT
# ifdef DW_CHARS
	  if (!curr->w_mbcs)
# endif
	    curr->w_rend.font = curr->w_FontL;	/* Default: GL */
#endif

	  /* The next part is only for speedup */
	  if (curr->w_state == LIT &&
#ifdef UTF8
	      curr->w_encoding != UTF8 &&
#endif
#ifdef DW_CHARS
	      !is_dw_font(curr->w_rend.font) &&
# ifdef ENCODINGS
	      curr->w_rend.font != KANA && !curr->w_mbcs &&
# endif
#endif
#ifdef FONT
	      curr->w_rend.font != '<' &&
#endif
	      c >= ' ' && c != 0x7f &&
	      ((c & 0x80) == 0 || ((c >= 0xa0 || !curr->w_c1) && !curr->w_gr)) && !curr->w_ss &&
	      !curr->w_insert && curr->w_x < cols - 1)
	    {
	      register int currx = curr->w_x;
	      char *imp = buf - 1;

	      while (currx < cols - 1)
		{
		  currx++;
		  if (--len == 0)
		    break;
		  c = (unsigned char)*buf++;
		  if (c < ' ' || c == 0x7f || ((c & 0x80) && ((c < 0xa0 && curr->w_c1) || curr->w_gr)))
		    break;
		}
	      currx -= curr->w_x;
	      if (currx > 0)
		{
		  MPutStr(curr, imp, currx, &curr->w_rend, curr->w_x, curr->w_y);
		  LPutStr(&curr->w_layer, imp, currx, &curr->w_rend, curr->w_x, curr->w_y);
		  curr->w_x += currx;
		}
	      if (len == 0)
		break;
	    }
	  /* end of speedup code */

#ifdef UTF8
	  if (curr->w_encoding == UTF8)
	    {
	      c = FromUtf8(c, &curr->w_decodestate);
	      if (c == -1)
		continue;
	      if (c == -2)
		{
		  c = UCS_REPL;
		  /* try char again */
		  buf--;
		  len++;
		}
	      if (c > 0xff)
		debug1("read UNICODE %04x\n", c);
	    }
#endif

	tryagain:
	  switch (curr->w_state)
	    {
	    case PRIN:
	      switch (c)
		{
		case '\033':
		  curr->w_state = PRINESC;
		  break;
		default:
		  PrintChar(c);
		}
	      break;
	    case PRINESC:
	      switch (c)
		{
		case '[':
		  curr->w_state = PRINCSI;
		  break;
		default:
		  PrintChar('\033');
		  PrintChar(c);
		  curr->w_state = PRIN;
		}
	      break;
	    case PRINCSI:
	      switch (c)
		{
		case '4':
		  curr->w_state = PRIN4;
		  break;
		default:
		  PrintChar('\033');
		  PrintChar('[');
		  PrintChar(c);
		  curr->w_state = PRIN;
		}
	      break;
	    case PRIN4:
	      switch (c)
		{
		case 'i':
		  curr->w_state = LIT;
		  PrintFlush();
		  if (curr->w_pdisplay && curr->w_pdisplay->d_printfd >= 0)
		    {
		      close(curr->w_pdisplay->d_printfd);
		      curr->w_pdisplay->d_printfd = -1;
		    }
		  curr->w_pdisplay = 0;
		  break;
		default:
		  PrintChar('\033');
		  PrintChar('[');
		  PrintChar('4');
		  PrintChar(c);
		  curr->w_state = PRIN;
		}
	      break;
	    case ASTR:
	      if (c == 0)
		break;
	      if (c == '\033')
		{
		  curr->w_state = STRESC;
		  break;
		}
	      /* special xterm hack: accept SetStatus sequence. Yucc! */
	      /* allow ^E for title escapes */
	      if (!(curr->w_StringType == OSC && c < ' ' && c != '\005'))
		if (!curr->w_c1 || c != ('\\' ^ 0xc0))
		  {
		    StringChar(c);
		    break;
		  }
	      c = '\\';
	      /* FALLTHROUGH */
	    case STRESC:
	      switch (c)
		{
		case '\\':
		  if (StringEnd() == 0 || len <= 1)
		    break;
		  /* check if somewhere a status is displayed */
		  for (cv = curr->w_layer.l_cvlist; cv; cv = cv->c_lnext)
		    {
		      display = cv->c_display;
		      if (D_status == STATUS_ON_WIN)
			break;
		    }
		  if (cv)
		    {
		      if (len > IOSIZE + 1)
			len = IOSIZE + 1;
		      curr->w_outlen = len - 1;
		      bcopy(buf, curr->w_outbuf, len - 1);
		      return;	/* wait till status is gone */
		    }
		  break;
		case '\033':
		  StringChar('\033');
		  break;
		default:
		  curr->w_state = ASTR;
		  StringChar('\033');
		  StringChar(c);
		  break;
		}
	      break;
	    case ESC:
	      switch (c)
		{
		case '[':
		  curr->w_NumArgs = 0;
		  curr->w_intermediate = 0;
		  bzero((char *) curr->w_args, MAXARGS * sizeof(int));
		  curr->w_state = CSI;
		  break;
		case ']':
		  StringStart(OSC);
		  break;
		case '_':
		  StringStart(APC);
		  break;
		case 'P':
		  StringStart(DCS);
		  break;
		case '^':
		  StringStart(PM);
		  break;
		case '!':
		  StringStart(GM);
		  break;
		case '"':
		case 'k':
		  StringStart(AKA);
		  break;
		default:
		  if (Special(c))
		    {
		      curr->w_state = LIT;
		      break;
		    }
		  debug1("not special. c = %x\n", c);
		  if (c >= ' ' && c <= '/')
		    {
		      if (curr->w_intermediate)
			{
#ifdef DW_CHARS
			  if (curr->w_intermediate == '$')
			    c |= '$' << 8;
			  else
#endif
			  c = -1;
			}
		      curr->w_intermediate = c;
		    }
		  else if (c >= '0' && c <= '~')
		    {
		      DoESC(c, curr->w_intermediate);
		      curr->w_state = LIT;
		    }
		  else
		    {
		      curr->w_state = LIT;
		      goto tryagain;
		    }
		}
	      break;
	    case CSI:
	      switch (c)
		{
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
		  if (curr->w_NumArgs >= 0 && curr->w_NumArgs < MAXARGS)
		    {
		      if (curr->w_args[curr->w_NumArgs] < 100000000)
			curr->w_args[curr->w_NumArgs] =
			  10 * curr->w_args[curr->w_NumArgs] + (c - '0');
		    }
		  break;
		case ';':
		case ':':
		  if (curr->w_NumArgs < MAXARGS)
		    curr->w_NumArgs++;
		  break;
		default:
		  if (Special(c))
		    break;
		  if (c >= '@' && c <= '~')
		    {
		      if (curr->w_NumArgs < MAXARGS)
			curr->w_NumArgs++;
		      DoCSI(c, curr->w_intermediate);
		      if (curr->w_state != PRIN)
			curr->w_state = LIT;
		    }
		  else if ((c >= ' ' && c <= '/') || (c >= '<' && c <= '?'))
		    curr->w_intermediate = curr->w_intermediate ? -1 : c;
		  else
		    {
		      curr->w_state = LIT;
		      goto tryagain;
		    }
		}
	      break;
	    case LIT:
	    default:
#ifdef DW_CHARS
	      if (curr->w_mbcs)
		if (c <= ' ' || c == 0x7f || (c >= 0x80 && c < 0xa0 && curr->w_c1))
		  curr->w_mbcs = 0;
#endif
	      if (c < ' ')
		{
		  if (c == '\033')
		    {
		      curr->w_intermediate = 0;
		      curr->w_state = ESC;
		      if (curr->w_autoaka < 0)
			curr->w_autoaka = 0;
		    }
		  else
		    Special(c);
		  break;
		}
	      if (c >= 0x80 && c < 0xa0 && curr->w_c1)
#ifdef FONT
		if ((curr->w_FontR & 0xf0) != 0x20
# ifdef UTF8
		       || curr->w_encoding == UTF8
# endif
		   )
#endif
		{
		  switch (c)
		    {
		    case 0xc0 ^ 'D':
		    case 0xc0 ^ 'E':
		    case 0xc0 ^ 'H':
		    case 0xc0 ^ 'M':
		    case 0xc0 ^ 'N':		/* SS2 */
		    case 0xc0 ^ 'O':		/* SS3 */
		      DoESC(c ^ 0xc0, 0);
		      break;
		    case 0xc0 ^ '[':
		      if (curr->w_autoaka < 0)
			curr->w_autoaka = 0;
		      curr->w_NumArgs = 0;
		      curr->w_intermediate = 0;
		      bzero((char *) curr->w_args, MAXARGS * sizeof(int));
		      curr->w_state = CSI;
		      break;
		    case 0xc0 ^ 'P':
		      StringStart(DCS);
		      break;
		    default:
		      break;
		    }
		  break;
		}

#ifdef FONT
# ifdef DW_CHARS
	      if (!curr->w_mbcs)
		{
# endif
		  if (c < 0x80 || curr->w_gr == 0)
		    curr->w_rend.font = curr->w_FontL;
# ifdef ENCODINGS
		  else if (curr->w_gr == 2 && !curr->w_ss)
		    curr->w_rend.font = curr->w_FontE;
# endif
		  else
		    curr->w_rend.font = curr->w_FontR;
# ifdef DW_CHARS
		}
# endif
# ifdef UTF8
	      if (curr->w_encoding == UTF8)
		{
		  if (curr->w_rend.font == '0')
		    {
		      struct mchar mc, *mcp;

		      debug1("SPECIAL %x\n", c);
		      mc.image = c;
		      mc.mbcs = 0;
		      mc.font = '0';
		      mc.fontx = 0;
		      mcp = recode_mchar(&mc, 0, UTF8);
		      debug2("%02x %02x\n", mcp->image, mcp->font);
		      c = mcp->image | mcp->font << 8;
		    }
		  curr->w_rend.font = 0;
		}
	      if (curr->w_encoding == UTF8 && c >= 0x0300 && utf8_iscomb(c))
		{
		  int ox, oy;
		  struct mchar omc;

		  ox = curr->w_x - 1;
		  oy = curr->w_y;
		  if (ox < 0)
		    {
		      ox = curr->w_width - 1;
		      oy--;
		    }
		  if (oy < 0)
		    oy = 0;
		  copy_mline2mchar(&omc, &curr->w_mlines[oy], ox);
		  if (omc.image == 0xff && omc.font == 0xff && omc.fontx == 0)
		    {
		      ox--;
		      if (ox >= 0)
			{
			  copy_mline2mchar(&omc, &curr->w_mlines[oy], ox);
			  omc.mbcs = 0xff;
			}
		    }
		  if (ox >= 0)
		    {
		      utf8_handle_comb(c, &omc);
		      MFixLine(curr, oy, &omc);
		      copy_mchar2mline(&omc, &curr->w_mlines[oy], ox);
		      LPutChar(&curr->w_layer, &omc, ox, oy);
		      LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
		    }
		  break;
		}
#  ifdef DW_CHARS
		if (curr->w_encoding == UTF8 && utf8_isdouble(c))
		  curr->w_mbcs = 0xff;
#  endif
	      font = curr->w_rend.font;
# endif
# ifdef DW_CHARS
#  ifdef ENCODINGS
	      if (font == KANA && curr->w_encoding == SJIS && curr->w_mbcs == 0)
		{
		  /* Lets see if it is the first byte of a kanji */
		  debug1("%x may be first of SJIS\n", c);
		  if ((0x81 <= c && c <= 0x9f) || (0xe0 <= c && c <= 0xef))
		    {
		      debug("YES!\n");
		      curr->w_mbcs = c;
		      break;
		    }
		}
#  endif
	      if (font == 031 && c == 0x80 && !curr->w_mbcs)
		font = curr->w_rend.font = 0;
	      if (is_dw_font(font) && c == ' ')
		font = curr->w_rend.font = 0;
	      if (is_dw_font(font) || curr->w_mbcs)
		{
		  int t = c;
		  if (curr->w_mbcs == 0)
		    {
		      curr->w_mbcs = c;
		      break;
		    }
		  if (curr->w_x == cols - 1)
		    {
		      curr->w_x += curr->w_wrap ? 1 : -1;
		      debug1("Patched w_x to %d\n", curr->w_x);
		    }
#  ifdef UTF8
		  if (curr->w_encoding != UTF8)
#  endif
		    {
		      c = curr->w_mbcs;
#  ifdef ENCODINGS
		      if (font == KANA && curr->w_encoding == SJIS)
			{
			  debug2("SJIS !! %x %x\n", c, t);
			  /*
			   * SJIS -> EUC mapping:
			   *   First byte:
			   *     81,82...9f -> 21,23...5d
			   *     e0,e1...ef -> 5f,61...7d
			   *   Second byte:
			   *     40-7e -> 21-5f
			   *     80-9e -> 60-7e
			   *     9f-fc -> 21-7e (increment first byte!)
			   */
			  if (0x40 <= t && t <= 0xfc && t != 0x7f)
			    {
			      if (c <= 0x9f) c = (c - 0x81) * 2 + 0x21;
			      else c = (c - 0xc1) * 2 + 0x21;
			      if (t <= 0x7e) t -= 0x1f;
			      else if (t <= 0x9e) t -= 0x20;
			      else t -= 0x7e, c++;
			      curr->w_rend.font = KANJI;
			    }
			  else
			    {
			      /* Incomplete shift-jis - skip first byte */
			      c = t;
			      t = 0;
			    }
			  debug2("SJIS after %x %x\n", c, t);
			}
#  endif
		      if (t && curr->w_gr && font != 030 && font != 031)
			{
			  t &= 0x7f;
			  if (t < ' ')
			    goto tryagain;
			}
		      if (t == '\177')
			break;
		      curr->w_mbcs = t;
		    }
		}
# endif	/* DW_CHARS */
	      if (font == '<' && c >= ' ')
		{
		  font = curr->w_rend.font = 0;
		  c |= 0x80;
		}
# ifdef UTF8
	      else if (curr->w_gr && curr->w_encoding != UTF8)
# else
	      else if (curr->w_gr)
# endif
		{
#ifdef ENCODINGS
		  if (c == 0x80 && font == 0 && curr->w_encoding == GBK)
		    c = 0xa4;
		  else
		    c &= 0x7f;
		  if (c < ' ' && font != 031)
		    goto tryagain;
#else
		  c &= 0x7f;
		  if (c < ' ')	/* this is ugly but kanji support */
		    goto tryagain;	/* prevents nicer programming */
#endif
		}
#endif /* FONT */
	      if (c == '\177')
		break;
	      curr->w_rend.image = c;
#ifdef UTF8
	      if (curr->w_encoding == UTF8)
		{
		  curr->w_rend.font = c >> 8;
		  curr->w_rend.fontx = c >> 16;
		}
#endif
#ifdef DW_CHARS
	      curr->w_rend.mbcs = curr->w_mbcs;
#endif
	      if (curr->w_x < cols - 1)
		{
		  if (curr->w_insert)
		    {
		      save_mline(&curr->w_mlines[curr->w_y], cols);
		      MInsChar(curr, &curr->w_rend, curr->w_x, curr->w_y);
		      LInsChar(&curr->w_layer, &curr->w_rend, curr->w_x, curr->w_y, &mline_old);
		      curr->w_x++;
		    }
		  else
		    {
		      MPutChar(curr, &curr->w_rend, curr->w_x, curr->w_y);
		      LPutChar(&curr->w_layer, &curr->w_rend, curr->w_x, curr->w_y);
		      curr->w_x++;
		    }
		}
	      else if (curr->w_x == cols - 1)
		{
		  MPutChar(curr, &curr->w_rend, curr->w_x, curr->w_y);
		  LPutChar(&curr->w_layer, &curr->w_rend, curr->w_x, curr->w_y);
		  if (curr->w_wrap)
		    curr->w_x++;
		}
	      else
		{
		  MWrapChar(curr, &curr->w_rend, curr->w_y, curr->w_top, curr->w_bot, curr->w_insert);
		  LWrapChar(&curr->w_layer, &curr->w_rend, curr->w_y, curr->w_top, curr->w_bot, curr->w_insert);
		  if (curr->w_y != curr->w_bot && curr->w_y != curr->w_height - 1)
		    curr->w_y++;
		  curr->w_x = 1;
		}
#ifdef FONT
# ifdef DW_CHARS
	      if (curr->w_mbcs)
		{
		  curr->w_rend.mbcs = curr->w_mbcs = 0;
		  curr->w_x++;
		}
# endif
	      if (curr->w_ss)
		{
		  curr->w_FontL = curr->w_charsets[curr->w_Charset];
		  curr->w_FontR = curr->w_charsets[curr->w_CharsetR];
		  curr->w_rend.font = curr->w_FontL;
		  LSetRendition(&curr->w_layer, &curr->w_rend);
		  curr->w_ss = 0;
		}
#endif /* FONT */
	      break;
	    }
	}
      while (--len);
    }
  if (!printcmd && curr->w_state == PRIN)
    PrintFlush();
}

static void
WLogString(p, buf, len)
struct win *p;
char *buf;
int len;
{
  if (!p->w_log)
    return;
  if (logtstamp_on && p->w_logsilence >= logtstamp_after * 2)
    {
      char *t = MakeWinMsg(logtstamp_string, p, '%');
      logfwrite(p->w_log, t, strlen(t));	/* long time no write */
    }
  p->w_logsilence = 0;
  if (logfwrite(p->w_log, buf, len) < 1)
    {
      WMsg(p, errno, "Error writing logfile");
      logfclose(p->w_log);
      p->w_log = 0;
    }
  if (!log_flush)
    logfflush(p->w_log);
}

static int
Special(c)
register int c;
{
  switch (c)
    {
    case '\b':
      BackSpace();
      return 1;
    case '\r':
      Return();
      return 1;
    case '\n':
      if (curr->w_autoaka)
	FindAKA();
    case '\013':	/* Vertical tab is the same as Line Feed */
      LineFeed(0);
      return 1;
    case '\007':
      WBell(curr, visual_bell);
      return 1;
    case '\t':
      ForwardTab();
      return 1;
#ifdef FONT
    case '\017':		/* SI */
      MapCharset(G0);
      return 1;
    case '\016':		/* SO */
      MapCharset(G1);
      return 1;
#endif
    }
  return 0;
}

static void
DoESC(c, intermediate)
int c, intermediate;
{
  debug2("DoESC: %x - inter = %x\n", c, intermediate);
  switch (intermediate)
    {
    case 0:
      switch (c)
	{
	case 'E':
	  LineFeed(1);
	  break;
	case 'D':
	  LineFeed(0);
	  break;
	case 'M':
	  ReverseLineFeed();
	  break;
	case 'H':
	  curr->w_tabs[curr->w_x] = 1;
	  break;
	case 'Z':		/* jph: Identify as VT100 */
	  Report("\033[?%d;%dc", 1, 2);
	  break;
	case '7':
	  SaveCursor(&curr->w_saved);
	  break;
	case '8':
	  RestoreCursor(&curr->w_saved);
	  break;
	case 'c':
	  ClearScreen();
	  ResetWindow(curr);
	  LKeypadMode(&curr->w_layer, 0);
	  LCursorkeysMode(&curr->w_layer, 0);
#ifndef TIOCPKT
	  WNewAutoFlow(curr, 1);
#endif
	  /* XXX
          SetRendition(&mchar_null);
	  InsertMode(0);
	  ChangeScrollRegion(0, rows - 1);
	  */
	  LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
	  break;
	case '=':
	  LKeypadMode(&curr->w_layer, curr->w_keypad = 1);
#ifndef TIOCPKT
	  WNewAutoFlow(curr, 0);
#endif /* !TIOCPKT */
	  break;
	case '>':
	  LKeypadMode(&curr->w_layer, curr->w_keypad = 0);
#ifndef TIOCPKT
	  WNewAutoFlow(curr, 1);
#endif /* !TIOCPKT */
	  break;
#ifdef FONT
	case 'n':		/* LS2 */
	  MapCharset(G2);
	  break;
	case 'o':		/* LS3 */
	  MapCharset(G3);
	  break;
	case '~':
	  MapCharsetR(G1);	/* LS1R */
	  break;
	/* { */
	case '}':
	  MapCharsetR(G2);	/* LS2R */
	  break;
	case '|':
	  MapCharsetR(G3);	/* LS3R */
	  break;
	case 'N':		/* SS2 */
	  if (curr->w_charsets[curr->w_Charset] != curr->w_charsets[G2]
	      || curr->w_charsets[curr->w_CharsetR] != curr->w_charsets[G2])
	    curr->w_FontR = curr->w_FontL = curr->w_charsets[curr->w_ss = G2];
	  else
	    curr->w_ss = 0;
	  break;
	case 'O':		/* SS3 */
	  if (curr->w_charsets[curr->w_Charset] != curr->w_charsets[G3]
	      || curr->w_charsets[curr->w_CharsetR] != curr->w_charsets[G3])
	    curr->w_FontR = curr->w_FontL = curr->w_charsets[curr->w_ss = G3];
	  else
	    curr->w_ss = 0;
	  break;
#endif /* FONT */
        case 'g':		/* VBELL, private screen sequence */
	  WBell(curr, 1);
          break;
	}
      break;
    case '#':
      switch (c)
	{
	case '8':
	  FillWithEs();
	  break;
	}
      break;
#ifdef FONT
    case '(':
      DesignateCharset(c, G0);
      break;
    case ')':
      DesignateCharset(c, G1);
      break;
    case '*':
      DesignateCharset(c, G2);
      break;
    case '+':
      DesignateCharset(c, G3);
      break;
# ifdef DW_CHARS
/*
 * ESC $ ( Fn: invoke multi-byte charset, Fn, to G0
 * ESC $ Fn: same as above.  (old sequence)
 * ESC $ ) Fn: invoke multi-byte charset, Fn, to G1
 * ESC $ * Fn: invoke multi-byte charset, Fn, to G2
 * ESC $ + Fn: invoke multi-byte charset, Fn, to G3
 */
    case '$':
    case '$'<<8 | '(':
      DesignateCharset(c & 037, G0);
      break;
    case '$'<<8 | ')':
      DesignateCharset(c & 037, G1);
      break;
    case '$'<<8 | '*':
      DesignateCharset(c & 037, G2);
      break;
    case '$'<<8 | '+':
      DesignateCharset(c & 037, G3);
      break;
# endif
#endif /* FONT */
    }
}

static void
DoCSI(c, intermediate)
int c, intermediate;
{
  register int i, a1 = curr->w_args[0], a2 = curr->w_args[1];

  if (curr->w_NumArgs > MAXARGS)
    curr->w_NumArgs = MAXARGS;
  switch (intermediate)
    {
    case 0:
      switch (c)
	{
	case 'H':
	case 'f':
	  if (a1 < 1)
	    a1 = 1;
	  if (curr->w_origin)
	    a1 += curr->w_top;
	  if (a1 > rows)
	    a1 = rows;
	  if (a2 < 1)
	    a2 = 1;
	  if (a2 > cols)
	    a2 = cols;
	  LGotoPos(&curr->w_layer, --a2, --a1);
	  curr->w_x = a2;
	  curr->w_y = a1;
	  if (curr->w_autoaka)
	    curr->w_autoaka = a1 + 1;
	  break;
	case 'J':
	  if (a1 < 0 || a1 > 2)
	    a1 = 0;
	  switch (a1)
	    {
	    case 0:
	      ClearToEOS();
	      break;
	    case 1:
	      ClearFromBOS();
	      break;
	    case 2:
	      ClearScreen();
	      LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
	      break;
	    }
	  break;
	case 'K':
	  if (a1 < 0 || a1 > 2)
	    a1 %= 3;
	  switch (a1)
	    {
	    case 0:
	      ClearLineRegion(curr->w_x, cols - 1);
	      break;
	    case 1:
	      ClearLineRegion(0, curr->w_x);
	      break;
	    case 2:
	      ClearLineRegion(0, cols - 1);
	      break;
	    }
	  break;
	case 'X':
	  a1 = curr->w_x + (a1 ? a1 - 1 : 0);
	  ClearLineRegion(curr->w_x, a1 < cols ? a1 : cols - 1);
	  break;
	case 'A':
	  CursorUp(a1 ? a1 : 1);
	  break;
	case 'B':
	  CursorDown(a1 ? a1 : 1);
	  break;
	case 'C':
	  CursorRight(a1 ? a1 : 1);
	  break;
	case 'D':
	  CursorLeft(a1 ? a1 : 1);
	  break;
	case 'E':
	  curr->w_x = 0;
	  CursorDown(a1 ? a1 : 1);	/* positions cursor */
	  break;
	case 'F':
	  curr->w_x = 0;
	  CursorUp(a1 ? a1 : 1);	/* positions cursor */
	  break;
	case 'G':
	case '`':			/* HPA */
	  curr->w_x = a1 ? a1 - 1 : 0;
	  if (curr->w_x >= cols)
	    curr->w_x = cols - 1;
	  LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
	  break;
	case 'd':			/* VPA */
	  curr->w_y = a1 ? a1 - 1 : 0;
	  if (curr->w_y >= rows)
	    curr->w_y = rows - 1;
	  LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
	  break;
	case 'm':
	  SelectRendition();
	  break;
	case 'g':
	  if (a1 == 0)
	    curr->w_tabs[curr->w_x] = 0;
	  else if (a1 == 3)
	    bzero(curr->w_tabs, cols);
	  break;
	case 'r':
	  if (!a1)
	    a1 = 1;
	  if (!a2)
	    a2 = rows;
	  if (a1 < 1 || a2 > rows || a1 >= a2)
	    break;
	  curr->w_top = a1 - 1;
	  curr->w_bot = a2 - 1;
	  /* ChangeScrollRegion(curr->w_top, curr->w_bot); */
	  if (curr->w_origin)
	    {
	      curr->w_y = curr->w_top;
	      curr->w_x = 0;
	    }
	  else
	    curr->w_y = curr->w_x = 0;
	  LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
	  break;
	case 's':
	  SaveCursor(&curr->w_saved);
	  break;
	case 't':
	  switch(a1)
	    {
	    case 11:
	      if (curr->w_layer.l_cvlist)
		Report("\033[1t", 0, 0);
	      else
		Report("\033[2t", 0, 0);
	      break;
	    case 7:
	      LRefreshAll(&curr->w_layer, 0);
	      break;
	    case 21:
	      a1 = strlen(curr->w_title);
	      if ((unsigned)(curr->w_inlen + 5 + a1) <= sizeof(curr->w_inbuf))
		{
		  bcopy("\033]l", curr->w_inbuf + curr->w_inlen, 3);
		  bcopy(curr->w_title, curr->w_inbuf + curr->w_inlen + 3, a1);
		  bcopy("\033\\", curr->w_inbuf + curr->w_inlen + 3 + a1, 2);
		  curr->w_inlen += 5 + a1;
		}
	      break;
	    case 8:
	      a1 = curr->w_args[2];
	      if (a1 < 1)
		a1 = curr->w_width;
	      if (a2 < 1)
		a2 = curr->w_height;
	      if (a1 > 10000 || a2 > 10000)
		break;
	      WChangeSize(curr, a1, a2);
	      cols = curr->w_width;
	      rows = curr->w_height;
	      break;
	    default:
	      break;
	    }
	  break;
	case 'u':
	  RestoreCursor(&curr->w_saved);
	  break;
	case 'I':
	  if (!a1)
	    a1 = 1;
	  while (a1--)
	    ForwardTab();
	  break;
	case 'Z':
	  if (!a1)
	    a1 = 1;
	  while (a1--)
	    BackwardTab();
	  break;
	case 'L':
	  InsertLine(a1 ? a1 : 1);
	  break;
	case 'M':
	  DeleteLine(a1 ? a1 : 1);
	  break;
	case 'P':
	  DeleteChar(a1 ? a1 : 1);
	  break;
	case '@':
	  InsertChar(a1 ? a1 : 1);
	  break;
	case 'h':
	  ASetMode(1);
	  break;
	case 'l':
	  ASetMode(0);
	  break;
	case 'i':		/* MC Media Control */
	  if (a1 == 5)
	    PrintStart();
	  break;
	case 'n':
	  if (a1 == 5)		/* Report terminal status */
	    Report("\033[0n", 0, 0);
	  else if (a1 == 6)		/* Report cursor position */
	    Report("\033[%d;%dR", curr->w_y + 1, curr->w_x + 1);
	  break;
	case 'c':		/* Identify as VT100 */
	  if (a1 == 0)
	    Report("\033[?%d;%dc", 1, 2);
	  break;
	case 'x':		/* decreqtparm */
	  if (a1 == 0 || a1 == 1)
	    Report("\033[%d;1;1;112;112;1;0x", a1 + 2, 0);
	  break;
	case 'p':		/* obscure code from a 97801 term */
	  if (a1 == 6 || a1 == 7)
	    {
	      curr->w_curinv = 7 - a1;
	      LCursorVisibility(&curr->w_layer, curr->w_curinv ? -1 : curr->w_curvvis);
	    }
	  break;
	case 'S':		/* code from a 97801 term / DEC vt400 */
	  ScrollRegion(a1 ? a1 : 1);
	  break;
	case 'T':		/* code from a 97801 term / DEC vt400 */
	case '^':		/* SD as per ISO 6429 */
	  ScrollRegion(a1 ? -a1 : -1);
	  break;
	}
      break;
    case '?':
      for (a2 = 0; a2 < curr->w_NumArgs; a2++)
	{
	  a1 = curr->w_args[a2];
	  debug2("\\E[?%d%c\n",a1,c);
	  if (c != 'h' && c != 'l')
	    break;
	  i = (c == 'h');
	  switch (a1)
	    {
	    case 1:	/* CKM:  cursor key mode */
	      LCursorkeysMode(&curr->w_layer, curr->w_cursorkeys = i);
#ifndef TIOCPKT
	      WNewAutoFlow(curr, !i);
#endif /* !TIOCPKT */
	      break;
	    case 2:	/* ANM:  ansi/vt52 mode */
	      if (i)
		{
#ifdef FONT
# ifdef ENCODINGS
		  if (curr->w_encoding)
		    break;
# endif
		  curr->w_charsets[0] = curr->w_charsets[1] =
		    curr->w_charsets[2] = curr->w_charsets[3] =
		    curr->w_FontL = curr->w_FontR = ASCII;
		  curr->w_Charset = 0;
		  curr->w_CharsetR = 2;
		  curr->w_ss = 0;
#endif
		}
	      break;
	    case 3:	/* COLM: column mode */
	      i = (i ? Z0width : Z1width);
	      ClearScreen();
	      curr->w_x = 0;
	      curr->w_y = 0;
	      WChangeSize(curr, i, curr->w_height);
	      cols = curr->w_width;
	      rows = curr->w_height;
	      break;
	 /* case 4:	   SCLM: scrolling mode */
	    case 5:	/* SCNM: screen mode */
	      if (i != curr->w_revvid)
	        WReverseVideo(curr, i);
	      curr->w_revvid = i;
	      break;
	    case 6:	/* OM:   origin mode */
	      if ((curr->w_origin = i) != 0)
		{
		  curr->w_y = curr->w_top;
		  curr->w_x = 0;
		}
	      else
		curr->w_y = curr->w_x = 0;
	      LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
	      break;
	    case 7:	/* AWM:  auto wrap mode */
	      curr->w_wrap = i;
	      break;
	 /* case 8:	   ARM:  auto repeat mode */
	 /* case 9:	   INLM: interlace mode */
	    case 9:	/* X10 mouse tracking */
	      curr->w_mouse = i ? 9 : 0;
	      LMouseMode(&curr->w_layer, curr->w_mouse);
	      break;
	 /* case 10:	   EDM:  edit mode */
	 /* case 11:	   LTM:  line transmit mode */
	 /* case 13:	   SCFDM: space compression / field delimiting */
	 /* case 14:	   TEM:  transmit execution mode */
	 /* case 16:	   EKEM: edit key execution mode */
	 /* case 18:	   PFF:  Printer term form feed */
	 /* case 19:	   PEX:  Printer extend screen / scroll. reg */
	    case 25:	/* TCEM: text cursor enable mode */
	      curr->w_curinv = !i;
	      LCursorVisibility(&curr->w_layer, curr->w_curinv ? -1 : curr->w_curvvis);
	      break;
	 /* case 34:	   RLM:  Right to left mode */
	 /* case 35:	   HEBM: hebrew keyboard map */
	 /* case 36:	   HEM:  hebrew encoding */
	 /* case 38:	         TeK Mode */
	 /* case 40:	         132 col enable */
	 /* case 42:	   NRCM: 7bit NRC character mode */
	 /* case 44:	         margin bell enable */
	    case 47:    /*       xterm-like alternate screen */
	    case 1047:  /*       xterm-like alternate screen */
	    case 1049:  /*       xterm-like alternate screen */
	      if (use_altscreen)
		{
		  if (i)
		    {
		      if (!curr->w_alt.on) {
			SaveCursor(&curr->w_alt.cursor);
			EnterAltScreen(curr);
		      }
		    }
		  else
		    {
		      if (curr->w_alt.on) {
		        RestoreCursor(&curr->w_alt.cursor);
		        LeaveAltScreen(curr);
		      }
		    }
		  if (a1 == 47 && !i)
		    curr->w_saved.on = 0;
		  LRefreshAll(&curr->w_layer, 0);
		  LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
		}
	      break;
	    case 1048:
	      if (i)
		SaveCursor(&curr->w_saved);
	      else
		RestoreCursor(&curr->w_saved);
	      break;
	 /* case 66:	   NKM:  Numeric keypad appl mode */
	 /* case 68:	   KBUM: Keyboard usage mode (data process) */
	    case 1000:	/* VT200 mouse tracking */
	    case 1001:	/* VT200 highlight mouse */
	    case 1002:	/* button event mouse*/
	    case 1003:	/* any event mouse*/
	      curr->w_mouse = i ? a1 : 0;
	      LMouseMode(&curr->w_layer, curr->w_mouse);
	      break;
         /* case 1005:     UTF-8 mouse mode rejected */
	    case 1006:  /* SGR mouse mode */
		curr->w_extmouse = i ? a1 : 0;
		LExtMouseMode(&curr->w_layer, curr->w_extmouse);
                break;
	 /* case 1015:     UXRVT mouse mode rejected */
	    }
	}
      break;
    case '>':
      switch (c)
	{
	case 'c':	/* secondary DA */
	  if (a1 == 0)
	    Report("\033[>%d;%d;0c", 83, nversion);	/* 83 == 'S' */
	  break;
	}
      break;
    }
}


static void
StringStart(type)
enum string_t type;
{
  curr->w_StringType = type;
  curr->w_stringp = curr->w_string;
  curr->w_state = ASTR;
}

static void
StringChar(c)
int c;
{
  if (curr->w_stringp >= curr->w_string + MAXSTR - 1)
    curr->w_state = LIT;
  else
    *(curr->w_stringp)++ = c;
}

/*
 * Do string processing. Returns -1 if output should be suspended
 * until status is gone.
 */
static int
StringEnd()
{
  struct canvas *cv;
  char *p;
  int typ;
  char *t;

  /* There's two ways to terminate an OSC. If we've seen an ESC
   * then it's been ST otherwise it's BEL. */
  t = curr->w_state == STRESC ? "\033\\" : "\a";

  curr->w_state = LIT;
  *curr->w_stringp = '\0';
  switch (curr->w_StringType)
    {
    case OSC:	/* special xterm compatibility hack */
      if (curr->w_string[0] == ';' || (p = index(curr->w_string, ';')) == 0)
	break;
      typ = atoi(curr->w_string);
      p++;
#ifdef MULTIUSER
      if (typ == 83)	/* 83 = 'S' */
	{
	  /* special execute commands sequence */
	  char *args[MAXARGS];
	  int argl[MAXARGS];
	  struct acluser *windowuser;

	  windowuser = *FindUserPtr(":window:");
	  if (windowuser && Parse(p, sizeof(curr->w_string) - (p - curr->w_string), args, argl))
	    {
	      for (display = displays; display; display = display->d_next)
		if (D_forecv->c_layer->l_bottom == &curr->w_layer)
		  break;	/* found it */
	      if (display == 0 && curr->w_layer.l_cvlist)
		display = curr->w_layer.l_cvlist->c_display;
	      if (display == 0)
		display = displays;
	      EffectiveAclUser = windowuser;
	      fore = curr;
	      flayer = fore->w_savelayer ? fore->w_savelayer : &fore->w_layer;
	      DoCommand(args, argl);
	      EffectiveAclUser = 0;
	      fore = 0;
	      flayer = 0;
	    }
	  break;
	}
#endif
#ifdef RXVT_OSC
      if (typ == 0 || typ == 1 || typ == 2 || typ == 11 || typ == 20 || typ == 39 || typ == 49)
	{
	  int typ2;
	  typ2 = typ / 10;
	  if (strcmp(curr->w_xtermosc[typ2], p))
	    {
	      if (typ != 11 || strcmp("?", p))
		{
		  strncpy(curr->w_xtermosc[typ2], p, sizeof(curr->w_xtermosc[typ2]) - 1);
		  curr->w_xtermosc[typ2][sizeof(curr->w_xtermosc[typ2]) - 1] = 0;
		}

	      for (display = displays; display; display = display->d_next)
		{
		  if (!D_CXT)
		    continue;
		  if (D_forecv->c_layer->l_bottom == &curr->w_layer)
		    SetXtermOSC(typ2, p, t);
		  if ((typ2 == 3 || typ2 == 4) && D_xtermosc[typ2])
		    Redisplay(0);
		  if (typ == 11 && !strcmp("?", p))
		    break;
		}
	    }
	}
      if (typ != 0 && typ != 2)
	break;
#else
      if (typ < 0 || typ > 2)
	break;
#endif
      
      curr->w_stringp -= p - curr->w_string;
      if (curr->w_stringp > curr->w_string)
	bcopy(p, curr->w_string, curr->w_stringp - curr->w_string);
      *curr->w_stringp = '\0';
      /* FALLTHROUGH */
    case APC:
      if (curr->w_hstatus)
	{
	  if (strcmp(curr->w_hstatus, curr->w_string) == 0)
	    break;	/* not changed */
	  free(curr->w_hstatus);
	  curr->w_hstatus = 0;
	}
      if (curr->w_string != curr->w_stringp)
	curr->w_hstatus = SaveStr(curr->w_string);
      WindowChanged(curr, 'h');
      break;
    case PM:
    case GM:
      for (display = displays; display; display = display->d_next)
	{
	  for (cv = D_cvlist; cv; cv = cv->c_next)
	    if (cv->c_layer->l_bottom == &curr->w_layer)
	      break;
	  if (cv || curr->w_StringType == GM)
	    MakeStatus(curr->w_string);
	}
      return -1;
    case DCS:
      LAY_DISPLAYS(&curr->w_layer, AddStr(curr->w_string));
      break;
    case AKA:
      if (curr->w_title == curr->w_akabuf && !*curr->w_string)
	break;
      if (curr->w_dynamicaka)
	ChangeAKA(curr, curr->w_string, strlen(curr->w_string));
      if (!*curr->w_string)
	curr->w_autoaka = curr->w_y + 1;
      break;
    default:
      break;
    }
  return 0;
}

static void
PrintStart()
{
  curr->w_pdisplay = 0;

  /* find us a nice display to print on, fore prefered */
  display = curr->w_lastdisp;
  if (!(display && curr == D_fore && (printcmd || D_PO)))
    for (display = displays; display; display = display->d_next)
      if (curr == D_fore && (printcmd || D_PO))
        break;
  if (!display)
    {
      struct canvas *cv;
      for (cv = curr->w_layer.l_cvlist; cv; cv = cv->c_lnext)
	{
	  display = cv->c_display;
	  if (printcmd || D_PO)
	    break;
	}
      if (!cv)
	{
	  display = displays;
	  if (!display || display->d_next || !(printcmd || D_PO))
	    return;
	}
    }
  curr->w_pdisplay = display;
  curr->w_stringp = curr->w_string;
  curr->w_state = PRIN;
  if (printcmd && curr->w_pdisplay->d_printfd < 0)
    curr->w_pdisplay->d_printfd = printpipe(curr, printcmd);
}

static void
PrintChar(c)
int c;
{
  if (curr->w_stringp >= curr->w_string + MAXSTR - 1)
    PrintFlush();
  *(curr->w_stringp)++ = c;
}

static void
PrintFlush()
{
  display = curr->w_pdisplay;
  if (display && printcmd)
    {
      char *bp = curr->w_string;
      int len = curr->w_stringp - curr->w_string;
      int r;
      while (len && display->d_printfd >= 0)
	{
	  r = write(display->d_printfd, bp, len);
	  if (r <= 0)
	    {
	      WMsg(curr, errno, "printing aborted");
	      close(display->d_printfd);
	      display->d_printfd = -1;
	      break;
	    }
	  bp += r;
	  len -= r;
	}
    }
  else if (display && curr->w_stringp > curr->w_string)
    {
      AddCStr(D_PO);
      AddStrn(curr->w_string, curr->w_stringp - curr->w_string);
      AddCStr(D_PF);
      Flush(3);
    }
  curr->w_stringp = curr->w_string;
}


void
WNewAutoFlow(win, on)
struct win *win;
int on;
{
  debug1("WNewAutoFlow: %d\n", on);
  if (win->w_flow & FLOW_AUTOFLAG)
    win->w_flow = FLOW_AUTOFLAG | (FLOW_AUTO|FLOW_NOW) * on;
  else
    win->w_flow = (win->w_flow & ~FLOW_AUTO) | FLOW_AUTO * on;
  LSetFlow(&win->w_layer, win->w_flow & FLOW_NOW);
}


#ifdef FONT

static void
DesignateCharset(c, n)
int c, n;
{
  curr->w_ss = 0;
# ifdef ENCODINGS
  if (c == ('@' & 037))		/* map JIS 6226 to 0208 */
    c = KANJI;
# endif
  if (c == 'B')
    c = ASCII;
  if (curr->w_charsets[n] != c)
    {
      curr->w_charsets[n] = c;
      if (curr->w_Charset == n)
	{
	  curr->w_FontL = c;
	  curr->w_rend.font = curr->w_FontL;
	  LSetRendition(&curr->w_layer, &curr->w_rend);
	}
      if (curr->w_CharsetR == n)
        curr->w_FontR = c;
    }
}

static void
MapCharset(n)
int n;
{
  curr->w_ss = 0;
  if (curr->w_Charset != n)
    {
      curr->w_Charset = n;
      curr->w_FontL = curr->w_charsets[n];
      curr->w_rend.font = curr->w_FontL;
      LSetRendition(&curr->w_layer, &curr->w_rend);
    }
}

static void
MapCharsetR(n)
int n;
{
  curr->w_ss = 0;
  if (curr->w_CharsetR != n)
    {
      curr->w_CharsetR = n;
      curr->w_FontR = curr->w_charsets[n];
    }
  curr->w_gr = 1;
}

#endif /* FONT */

static void
SaveCursor(cursor)
struct cursor *cursor;
{
  cursor->on = 1;
  cursor->x = curr->w_x;
  cursor->y = curr->w_y;
  cursor->Rend = curr->w_rend;
#ifdef FONT
  cursor->Charset = curr->w_Charset;
  cursor->CharsetR = curr->w_CharsetR;
  bcopy((char *) curr->w_charsets, (char *) cursor->Charsets,
	4 * sizeof(int));
#endif
}

static void
RestoreCursor(cursor)
struct cursor *cursor;
{
  if (!cursor->on)
    return;
  LGotoPos(&curr->w_layer, cursor->x, cursor->y);
  curr->w_x = cursor->x;
  curr->w_y = cursor->y;
  curr->w_rend = cursor->Rend;
#ifdef FONT
  bcopy((char *) cursor->Charsets, (char *) curr->w_charsets,
	4 * sizeof(int));
  curr->w_Charset = cursor->Charset;
  curr->w_CharsetR = cursor->CharsetR;
  curr->w_ss = 0;
  curr->w_FontL = curr->w_charsets[curr->w_Charset];
  curr->w_FontR = curr->w_charsets[curr->w_CharsetR];
#endif
  LSetRendition(&curr->w_layer, &curr->w_rend);
}

static void
BackSpace()
{
  if (curr->w_x > 0)
    {
      curr->w_x--;
    }
  else if (curr->w_wrap && curr->w_y > 0)
    {
      curr->w_x = cols - 1;
      curr->w_y--;
    }
  LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
}

static void
Return()
{
  if (curr->w_x == 0)
    return;
  curr->w_x = 0;
  LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
}

static void
LineFeed(out_mode)
int out_mode;
{
  /* out_mode: 0=lf, 1=cr+lf */
  if (out_mode)
    curr->w_x = 0;
  if (curr->w_y != curr->w_bot)		/* Don't scroll */
    {
      if (curr->w_y < rows-1)
	curr->w_y++;
      LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
      return;
    }
  if (curr->w_autoaka > 1)
    curr->w_autoaka--;
  MScrollV(curr, 1, curr->w_top, curr->w_bot, CURR_BCE);
  LScrollV(&curr->w_layer, 1, curr->w_top, curr->w_bot, CURR_BCE);
  LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
}

static void
ReverseLineFeed()
{
  if (curr->w_y == curr->w_top)
    {
      MScrollV(curr, -1, curr->w_top, curr->w_bot, CURR_BCE);
      LScrollV(&curr->w_layer, -1, curr->w_top, curr->w_bot, CURR_BCE);
      LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
    }
  else if (curr->w_y > 0)
    CursorUp(1);
}

static void
InsertChar(n)
int n;
{
  register int y = curr->w_y, x = curr->w_x;

  if (n <= 0)
    return;
  if (x == cols)
    x--;
  save_mline(&curr->w_mlines[y], cols);
  MScrollH(curr, -n, y, x, curr->w_width - 1, CURR_BCE);
  LScrollH(&curr->w_layer, -n, y, x, curr->w_width - 1, CURR_BCE, &mline_old);
  LGotoPos(&curr->w_layer, x, y);
}

static void
DeleteChar(n)
int n;
{
  register int y = curr->w_y, x = curr->w_x;

  if (x == cols)
    x--;
  save_mline(&curr->w_mlines[y], cols);
  MScrollH(curr, n, y, x, curr->w_width - 1, CURR_BCE);
  LScrollH(&curr->w_layer, n, y, x, curr->w_width - 1, CURR_BCE, &mline_old);
  LGotoPos(&curr->w_layer, x, y);
}

static void
DeleteLine(n)
int n;
{
  if (curr->w_y < curr->w_top || curr->w_y > curr->w_bot)
    return;
  if (n > curr->w_bot - curr->w_y + 1)
    n = curr->w_bot - curr->w_y + 1;
  MScrollV(curr, n, curr->w_y, curr->w_bot, CURR_BCE);
  LScrollV(&curr->w_layer, n, curr->w_y, curr->w_bot, CURR_BCE);
  LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
}

static void
InsertLine(n)
int n;
{
  if (curr->w_y < curr->w_top || curr->w_y > curr->w_bot)
    return;
  if (n > curr->w_bot - curr->w_y + 1)
    n = curr->w_bot - curr->w_y + 1;
  MScrollV(curr, -n, curr->w_y, curr->w_bot, CURR_BCE);
  LScrollV(&curr->w_layer, -n, curr->w_y, curr->w_bot, CURR_BCE);
  LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
}

static void
ScrollRegion(n)
int n;
{
  MScrollV(curr, n, curr->w_top, curr->w_bot, CURR_BCE);
  LScrollV(&curr->w_layer, n, curr->w_top, curr->w_bot, CURR_BCE);
  LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
}


static void
ForwardTab()
{
  register int x = curr->w_x;

  if (x == cols)
    {
      LineFeed(1);
      x = 0;
    }
  if (curr->w_tabs[x] && x < cols - 1)
    x++;
  while (x < cols - 1 && !curr->w_tabs[x])
    x++;
  curr->w_x = x;
  LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
}

static void
BackwardTab()
{
  register int x = curr->w_x;

  if (curr->w_tabs[x] && x > 0)
    x--;
  while (x > 0 && !curr->w_tabs[x])
    x--;
  curr->w_x = x;
  LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
}

static void
ClearScreen()
{
  LClearArea(&curr->w_layer, 0, 0, curr->w_width - 1, curr->w_height - 1, CURR_BCE, 1);
#ifdef COPY_PASTE
  MScrollV(curr, curr->w_height, 0, curr->w_height - 1, CURR_BCE);
#else
  MClearArea(curr, 0, 0, curr->w_width - 1, curr->w_height - 1, CURR_BCE);
#endif
}

static void
ClearFromBOS()
{
  register int y = curr->w_y, x = curr->w_x;

  LClearArea(&curr->w_layer, 0, 0, x, y, CURR_BCE, 1);
  MClearArea(curr, 0, 0, x, y, CURR_BCE);
  RestorePosRendition();
}

static void
ClearToEOS()
{
  register int y = curr->w_y, x = curr->w_x;

  if (x == 0 && y == 0)
    {
      ClearScreen();
      RestorePosRendition();
      return;
    }
  LClearArea(&curr->w_layer, x, y, cols - 1, rows - 1, CURR_BCE, 1);
  MClearArea(curr, x, y, cols - 1, rows - 1, CURR_BCE);
  RestorePosRendition();
}

static void
ClearLineRegion(from, to)
int from, to;
{
  register int y = curr->w_y;
  LClearArea(&curr->w_layer, from, y, to, y, CURR_BCE, 1);
  MClearArea(curr, from, y, to, y, CURR_BCE);
  RestorePosRendition();
}

static void
CursorRight(n)
register int n;
{
  register int x = curr->w_x;

  if (x == cols)
    {
      LineFeed(1);
      x = 0;
    }
  if ((curr->w_x += n) >= cols)
    curr->w_x = cols - 1;
  LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
}

static void
CursorUp(n)
register int n;
{
  if (curr->w_y < curr->w_top)		/* if above scrolling rgn, */
    {
      if ((curr->w_y -= n) < 0)		/* ignore its limits      */
         curr->w_y = 0;
    }
  else
    if ((curr->w_y -= n) < curr->w_top)
      curr->w_y = curr->w_top;
  LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
}

static void
CursorDown(n)
register int n;
{
  if (curr->w_y > curr->w_bot)		/* if below scrolling rgn, */
    {
      if ((curr->w_y += n) > rows - 1)	/* ignore its limits      */
        curr->w_y = rows - 1;
    }
  else
    if ((curr->w_y += n) > curr->w_bot)
      curr->w_y = curr->w_bot;
  LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
}

static void
CursorLeft(n)
register int n;
{
  if ((curr->w_x -= n) < 0)
    curr->w_x = 0;
  LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
}

static void
ASetMode(on)
int on;
{
  register int i;

  for (i = 0; i < curr->w_NumArgs; ++i)
    {
      switch (curr->w_args[i])
	{
     /* case 2:		   KAM: Lock keyboard */
	case 4:		/* IRM: Insert mode */
	  curr->w_insert = on;
	  LAY_DISPLAYS(&curr->w_layer, InsertMode(on));
	  break;
     /* case 12:	   SRM: Echo mode on */
	case 20:	/* LNM: Linefeed mode */
	  curr->w_autolf = on;
	  break;
	case 34:
	  curr->w_curvvis = !on;
	  LCursorVisibility(&curr->w_layer, curr->w_curinv ? -1 : curr->w_curvvis);
	  break;
	default:
	  break;
	}
    }
}

static char rendlist[] =
{
  ~((1 << NATTR) - 1), A_BD, A_DI, A_SO, A_US, A_BL, 0, A_RV, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, ~(A_BD|A_SO|A_DI), ~A_SO, ~A_US, ~A_BL, 0, ~A_RV
};

static void
SelectRendition()
{
#ifdef COLOR
  register int j, i = 0, a = curr->w_rend.attr, c = curr->w_rend.color;
# ifdef COLORS256
  int cx = curr->w_rend.colorx;
# endif
#else
  register int j, i = 0, a = curr->w_rend.attr;
#endif

  do
    {
      j = curr->w_args[i];
#ifdef COLOR
      if ((j == 38 || j == 48) && i + 2 < curr->w_NumArgs && curr->w_args[i + 1] == 5)
	{
	  int jj;

	  i += 2;
	  jj = curr->w_args[i];
	  if (jj < 0 || jj > 255)
	    continue;
# ifdef COLORS256
	  if (j == 38)
	    {
	      c = (c & 0xf0) | ((jj & 0x0f) ^ 9);
	      a |= A_BFG;
	      if (jj >= 8 && jj < 16)
		c |= 0x08;
	      else
		a ^= A_BFG;
	      a = (a & 0xbf) | (jj & 8 ? 0x40 : 0);
	      cx = (cx & 0xf0) | (jj >> 4 & 0x0f);
	    }
	  else
	    {
	      c = (c & 0x0f) | ((jj & 0x0f) ^ 9) << 4;
	      a |= A_BBG;
	      if (jj >= 8 && jj < 16)
		c |= 0x80;
	      else
		a ^= A_BBG;
	      cx = (cx & 0x0f) | (jj & 0xf0);
	    }
	  continue;
# else
	  jj = color256to16(jj) + 30;
	  if (jj >= 38)
	    jj += 60 - 8;
	  j = j == 38 ? jj : jj + 10;
# endif
	}
# ifdef COLORS16
      if (j == 0 || (j >= 30 && j <= 39 && j != 38))
	a &= 0xbf;
      if (j == 0 || (j >= 40 && j <= 49 && j != 48))
	a &= 0x7f;
      if (j >= 90 && j <= 97)
	a |= 0x40;
      if (j >= 100 && j <= 107)
	a |= 0x80;
# endif
      if (j >= 90 && j <= 97)
	j -= 60;
      if (j >= 100 && j <= 107)
	j -= 60;
      if (j >= 30 && j <= 39 && j != 38)
	c = (c & 0xf0) | ((j - 30) ^ 9);
      else if (j >= 40 && j <= 49 && j != 48)
	c = (c & 0x0f) | (((j - 40) ^ 9) << 4);
      if (j == 0)
	c = 0;
# ifdef COLORS256
      if (j == 0 || (j >= 30 && j <= 39 && j != 38))
	cx &= 0xf0;
      if (j == 0 || (j >= 40 && j <= 49 && j != 48))
	cx &= 0x0f;
# endif
#endif
      if (j < 0 || j >= (int)(sizeof(rendlist)/sizeof(*rendlist)))
	continue;
      j = rendlist[j];
      if (j & (1 << NATTR))
        a &= j;
      else
        a |= j;
    }
  while (++i < curr->w_NumArgs);
  curr->w_rend.attr = a;
#ifdef COLOR
  curr->w_rend.color = c;
# ifdef COLORS256
  curr->w_rend.colorx = cx;
# endif
#endif
  LSetRendition(&curr->w_layer, &curr->w_rend);
}

static void
FillWithEs()
{
  register int i;
  register unsigned char *p, *ep;

  LClearAll(&curr->w_layer, 1);
  curr->w_y = curr->w_x = 0;
  for (i = 0; i < rows; ++i)
    {
      clear_mline(&curr->w_mlines[i], 0, cols + 1);
      p = curr->w_mlines[i].image;
      ep = p + cols;
      while (p < ep)
	*p++ = 'E';
    }
  LRefreshAll(&curr->w_layer, 1);
}


/*
 *  Ugly autoaka hack support:
 *    ChangeAKA() sets a new aka
 *    FindAKA() searches for an autoaka match
 */

void
ChangeAKA(p, s, l)
struct win *p;
char *s;
int l;
{
  int i, c;

  for (i = 0; l > 0; l--)
    {
      if (p->w_akachange + i == p->w_akabuf + sizeof(p->w_akabuf) - 1)
	break;
      c = (unsigned char)*s++;
      if (c == 0)
	break;
      if (c < 32 || c == 127 || (c >= 128 && c < 160 && p->w_c1))
	continue;
      p->w_akachange[i++] = c;
    }
  p->w_akachange[i] = 0;
  p->w_title = p->w_akachange;
  if (p->w_akachange != p->w_akabuf)
    if (p->w_akachange[0] == 0 || p->w_akachange[-1] == ':')
      p->w_title = p->w_akabuf + strlen(p->w_akabuf) + 1;
  WindowChanged(p, 't');
  WindowChanged((struct win *)0, 'w');
  WindowChanged((struct win *)0, 'W');
}

static void
FindAKA()
{
  register unsigned char *cp, *line;
  register struct win *wp = curr;
  register int len = strlen(wp->w_akabuf);
  int y;

  y = (wp->w_autoaka > 0 && wp->w_autoaka <= wp->w_height) ? wp->w_autoaka - 1 : wp->w_y;
  cols = wp->w_width;
 try_line:
  cp = line = wp->w_mlines[y].image;
  if (wp->w_autoaka > 0 &&  *wp->w_akabuf != '\0')
    {
      for (;;)
	{
	  if (cp - line >= cols - len)
	    {
	      if (++y == wp->w_autoaka && y < rows)
		goto try_line;
	      return;
	    }
	  if (strncmp((char *)cp, wp->w_akabuf, len) == 0)
	    break;
	  cp++;
	}
      cp += len;
    }
  for (len = cols - (cp - line); len && *cp == ' '; len--, cp++)
    ;
  if (len)
    {
      if (wp->w_autoaka > 0 && (*cp == '!' || *cp == '%' || *cp == '^'))
	wp->w_autoaka = -1;
      else
	wp->w_autoaka = 0;
      line = cp;
      while (len && *cp != ' ')
	{
	  if (*cp++ == '/')
	    line = cp;
	  len--;
	}
      ChangeAKA(wp, (char *)line, cp - line);
    }
  else
    wp->w_autoaka = 0;
}

static void
RestorePosRendition()
{
  LGotoPos(&curr->w_layer, curr->w_x, curr->w_y);
  LSetRendition(&curr->w_layer, &curr->w_rend);
}

/* Send a terminal report as if it were typed. */ 
static void
Report(fmt, n1, n2)
char *fmt;
int n1, n2;
{
  register int len;
  char rbuf[40];	/* enough room for all replys */

  sprintf(rbuf, fmt, n1, n2);
  len = strlen(rbuf);

#ifdef PSEUDOS
  if (W_UWP(curr))
    {
      if ((unsigned)(curr->w_pwin->p_inlen + len) <= sizeof(curr->w_pwin->p_inbuf))
	{
	  bcopy(rbuf, curr->w_pwin->p_inbuf + curr->w_pwin->p_inlen, len);
	  curr->w_pwin->p_inlen += len;
	}
    }
  else
#endif
    {
      if ((unsigned)(curr->w_inlen + len) <= sizeof(curr->w_inbuf))
	{
	  bcopy(rbuf, curr->w_inbuf + curr->w_inlen, len);
	  curr->w_inlen += len;
	}
    }
}



/*
 *====================================================================*
 *====================================================================*
 */

/**********************************************************************
 *
 * Memory subsystem.
 *
 */

static void
MFixLine(p, y, mc)
struct win *p;
int y;
struct mchar *mc;
{
  struct mline *ml = &p->w_mlines[y];
  if (mc->attr && ml->attr == null)
    {
      if ((ml->attr = (unsigned char *)calloc(p->w_width + 1, 1)) == 0)
	{
	  ml->attr = null;
	  mc->attr = p->w_rend.attr = 0;
	  WMsg(p, 0, "Warning: no space for attr - turned off");
	}
    }
#ifdef FONT
  if (mc->font && ml->font == null)
    {
      if ((ml->font = (unsigned char *)calloc(p->w_width + 1, 1)) == 0)
	{
	  ml->font = null;
	  p->w_FontL = p->w_charsets[p->w_ss ? p->w_ss : p->w_Charset] = 0;
	  p->w_FontR = p->w_charsets[p->w_ss ? p->w_ss : p->w_CharsetR] = 0;
	  mc->font = mc->fontx = p->w_rend.font  = 0;
	  WMsg(p, 0, "Warning: no space for font - turned off");
	}
    }
  if (mc->fontx && ml->fontx == null)
    {
      if ((ml->fontx = (unsigned char *)calloc(p->w_width + 1, 1)) == 0)
	{
	  ml->fontx = null;
	  mc->fontx = 0;
	}
    }
#endif
#ifdef COLOR
  if (mc->color && ml->color == null)
    {
      if ((ml->color = (unsigned char *)calloc(p->w_width + 1, 1)) == 0)
	{
	  ml->color = null;
	  mc->color = p->w_rend.color = 0;
	  WMsg(p, 0, "Warning: no space for color - turned off");
	}
    }
# ifdef COLORS256
  if (mc->colorx && ml->colorx == null)
    {
      if ((ml->colorx = (unsigned char *)calloc(p->w_width + 1, 1)) == 0)
	{
	  ml->colorx = null;
	  mc->colorx = p->w_rend.colorx = 0;
	  WMsg(p, 0, "Warning: no space for extended colors - turned off");
	}
    }
# endif
#endif
}

/*****************************************************************/

#ifdef DW_CHARS
# define MKillDwRight(p, ml, x)					\
  if (dw_right(ml, x, p->w_encoding))				\
    {								\
      if (x > 0)						\
	copy_mchar2mline(&mchar_blank, ml, x - 1);		\
      copy_mchar2mline(&mchar_blank, ml, x);			\
    }

# define MKillDwLeft(p, ml, x)					\
  if (dw_left(ml, x, p->w_encoding))				\
    {								\
      copy_mchar2mline(&mchar_blank, ml, x);			\
      copy_mchar2mline(&mchar_blank, ml, x + 1);		\
    }
#else
# define MKillDwRight(p, ml, x) ;
# define MKillDwLeft(p, ml, x) ;
#endif

static void
MScrollH(p, n, y, xs, xe, bce)
struct win *p;
int n, y, xs, xe, bce;
{
  struct mline *ml;

  if (n == 0)
    return;
  ml = &p->w_mlines[y];
  MKillDwRight(p, ml, xs);
  MKillDwLeft(p, ml, xe);
  if (n > 0)
    {
      if (xe - xs + 1 > n)
	{
	  MKillDwRight(p, ml, xs + n);
	  bcopy_mline(ml, xs + n, xs, xe + 1 - xs - n);
	}
      else
	n = xe - xs + 1;
      clear_mline(ml, xe + 1 - n, n);
#ifdef COLOR
      if (bce)
        MBceLine(p, y, xe + 1 - n, n, bce);
#endif
    }
  else
    {
      n = -n;
      if (xe - xs + 1 > n)
	{
	  MKillDwLeft(p, ml, xe - n);
	  bcopy_mline(ml, xs, xs + n, xe + 1 - xs - n);
	}
      else
	n = xe - xs + 1;
      clear_mline(ml, xs, n);
#ifdef COLOR
      if (bce)
        MBceLine(p, y, xs, n, bce);
#endif
    }
}

static void
MScrollV(p, n, ys, ye, bce)
struct win *p;
int n, ys, ye, bce;
{
  int i, cnt1, cnt2;
  struct mline tmp[256];
  struct mline *ml;

  if (n == 0)
    return;
  if (n > 0)
    {
      if (ye - ys + 1 < n)
	n = ye - ys + 1;
      if (n > 256)
	{
	  MScrollV(p, n - 256, ys, ye, bce);
	  n = 256;
	}
#ifdef COPY_PASTE
      if (compacthist)
	{
	  ye = MFindUsedLine(p, ye, ys);
	  if (ye - ys + 1 < n)
	    n = ye - ys + 1;
	  if (n <= 0)
	    return;
	}
#endif
      /* Clear lines */
      ml = p->w_mlines + ys;
      for (i = ys; i < ys + n; i++, ml++)
	{
#ifdef COPY_PASTE
	  if (ys == p->w_top)
	    WAddLineToHist(p, ml);
#endif
	  if (ml->attr != null)
	    free(ml->attr);
	  ml->attr = null;
#ifdef FONT
	  if (ml->font != null)
	    free(ml->font);
	  ml->font = null;
	  if (ml->fontx != null)
	    free(ml->fontx);
	  ml->fontx = null;
#endif
#ifdef COLOR
	  if (ml->color != null)
	    free(ml->color);
	  ml->color = null;
# ifdef COLORS256
	  if (ml->colorx != null)
	    free(ml->colorx);
	  ml->colorx = null;
# endif
#endif
	  bclear((char *)ml->image, p->w_width + 1);
#ifdef COLOR
	  if (bce)
	    MBceLine(p, i, 0, p->w_width, bce);
#endif
	}
      /* switch 'em over */
      cnt1 = n * sizeof(struct mline);
      cnt2 = (ye - ys + 1 - n) * sizeof(struct mline);
      if (cnt1 && cnt2)
	Scroll((char *)(p->w_mlines + ys), cnt1, cnt2, (char *)tmp);
    }
  else
    {
      n = -n;
      if (ye - ys + 1 < n)
	n = ye - ys + 1;
      if (n > 256)
	{
	  MScrollV(p, - (n - 256), ys, ye, bce);
	  n = 256;
	}

      ml = p->w_mlines + ye;
      /* Clear lines */
      for (i = ye; i > ye - n; i--, ml--)
	{
	  if (ml->attr != null)
	    free(ml->attr);
	  ml->attr = null;
#ifdef FONT
	  if (ml->font != null)
	    free(ml->font);
	  ml->font = null;
	  if (ml->fontx != null)
	    free(ml->fontx);
	  ml->fontx = null;
#endif
#ifdef COLOR
	  if (ml->color != null)
	    free(ml->color);
	  ml->color = null;
# ifdef COLORS256
	  if (ml->colorx != null)
	    free(ml->colorx);
	  ml->colorx = null;
# endif
#endif
	  bclear((char *)ml->image, p->w_width + 1);
#ifdef COLOR
	  if (bce)
	    MBceLine(p, i, 0, p->w_width, bce);
#endif
	}
      cnt1 = n * sizeof(struct mline);
      cnt2 = (ye - ys + 1 - n) * sizeof(struct mline);
      if (cnt1 && cnt2)
	Scroll((char *)(p->w_mlines + ys), cnt2, cnt1, (char *)tmp);
    }
}

static void
Scroll(cp, cnt1, cnt2, tmp)
char *cp, *tmp;
int cnt1, cnt2;
{
  if (!cnt1 || !cnt2)
    return;
  if (cnt1 <= cnt2)
    {
      bcopy(cp, tmp, cnt1);
      bcopy(cp + cnt1, cp, cnt2);
      bcopy(tmp, cp + cnt2, cnt1);
    }
  else
    {
      bcopy(cp + cnt1, tmp, cnt2);
      bcopy(cp, cp + cnt2, cnt1);
      bcopy(tmp, cp, cnt2);
    }
}

static void
MClearArea(p, xs, ys, xe, ye, bce)
struct win *p;
int xs, ys, xe, ye, bce;
{
  int n, y;
  int xxe;
  struct mline *ml;

  /* Check for zero-height window */
  if (ys < 0 || ye < ys)
    return;

  /* check for magic margin condition */
  if (xs >= p->w_width)
    xs = p->w_width - 1;
  if (xe >= p->w_width)
    xe = p->w_width - 1;

  MKillDwRight(p, p->w_mlines + ys, xs);
  MKillDwLeft(p, p->w_mlines + ye, xe);

  ml = p->w_mlines + ys;
  for (y = ys; y <= ye; y++, ml++)
    {
      xxe = (y == ye) ? xe : p->w_width - 1;
      n = xxe - xs + 1;
      if (n > 0)
	clear_mline(ml, xs, n);
#ifdef COLOR
      if (n > 0 && bce)
	MBceLine(p, y, xs, xs + n - 1, bce);
#endif
      xs = 0;
    }
}

static void
MInsChar(p, c, x, y)
struct win *p;
struct mchar *c;
int x, y;
{
  int n;
  struct mline *ml;

  ASSERT(x >= 0 && x < p->w_width);
  MFixLine(p, y, c);
  ml = p->w_mlines + y;
  n = p->w_width - x - 1;
  MKillDwRight(p, ml, x);
  if (n > 0)
    {
      MKillDwRight(p, ml, p->w_width - 1);
      bcopy_mline(ml, x, x + 1, n);
    }
  copy_mchar2mline(c, ml, x);
#ifdef DW_CHARS
  if (c->mbcs)
    {
      if (--n > 0)
        {
          MKillDwRight(p, ml, p->w_width - 1);
	  bcopy_mline(ml, x + 1, x + 2, n);
	}
      copy_mchar2mline(c, ml, x + 1);
      ml->image[x + 1] = c->mbcs;
# ifdef UTF8
      if (p->w_encoding != UTF8)
	ml->font[x + 1] |= 0x80;
      else if (p->w_encoding == UTF8 && c->mbcs)
	{
	  ml->font[x + 1] = c->mbcs;
	  ml->fontx[x + 1] = 0;
	}
# else
      ml->font[x + 1] |= 0x80;
# endif
    }
#endif
}

static void
MPutChar(p, c, x, y)
struct win *p;
struct mchar *c;
int x, y;
{
  struct mline *ml;

  MFixLine(p, y, c);
  ml = &p->w_mlines[y];
  MKillDwRight(p, ml, x);
  MKillDwLeft(p, ml, x);
  copy_mchar2mline(c, ml, x);
#ifdef DW_CHARS
  if (c->mbcs)
    {
      MKillDwLeft(p, ml, x + 1);
      copy_mchar2mline(c, ml, x + 1);
      ml->image[x + 1] = c->mbcs;
# ifdef UTF8
      if (p->w_encoding != UTF8)
	ml->font[x + 1] |= 0x80;
      else if (p->w_encoding == UTF8 && c->mbcs)
	{
	  ml->font[x + 1] = c->mbcs;
	  ml->fontx[x + 1] = 0;
	}
# else
      ml->font[x + 1] |= 0x80;
# endif
    }
#endif
}


static void
MWrapChar(p, c, y, top, bot, ins)
struct win *p;
struct mchar *c;
int y, top, bot;
int ins;
{
  struct mline *ml;
  int bce;

#ifdef COLOR
  bce = rend_getbg(c);
#else
  bce = 0;
#endif
  MFixLine(p, y, c);
  ml = &p->w_mlines[y];
  copy_mchar2mline(&mchar_null, ml, p->w_width);
  if (y == bot)
    MScrollV(p, 1, top, bot, bce);
  else if (y < p->w_height - 1)
    y++;
  if (ins)
    MInsChar(p, c, 0, y);
  else
    MPutChar(p, c, 0, y);
}

static void
MPutStr(p, s, n, r, x, y)
struct win *p;
char *s;
int n;
struct mchar *r;
int x, y;
{
  struct mline *ml;
  int i;
  unsigned char *b;

  if (n <= 0)
    return;
  MFixLine(p, y, r);
  ml = &p->w_mlines[y];
  MKillDwRight(p, ml, x);
  MKillDwLeft(p, ml, x + n - 1);
  bcopy(s, (char *)ml->image + x, n);
  if (ml->attr != null)
    {
      b = ml->attr + x;
      for (i = n; i-- > 0;)
	*b++ = r->attr;
    }
#ifdef FONT
  if (ml->font != null)
    {
      b = ml->font + x;
      for (i = n; i-- > 0;)
	*b++ = r->font;
    }
  if (ml->fontx != null)
    {
      b = ml->fontx + x;
      for (i = n; i-- > 0;)
	*b++ = r->fontx;
    }
#endif
#ifdef COLOR
  if (ml->color != null)
    {
      b = ml->color + x;
      for (i = n; i-- > 0;)
	*b++ = r->color;
    }
# ifdef COLORS256
  if (ml->colorx != null)
    {
      b = ml->colorx + x;
      for (i = n; i-- > 0;)
	*b++ = r->colorx;
    }
# endif
#endif
}

#ifdef COLOR
static void
MBceLine(p, y, xs, xe, bce)
struct win *p;
int y, xs, xe, bce;
{
  struct mchar mc;
  struct mline *ml;
  int x;

  mc = mchar_null;
  rend_setbg(&mc, bce);
  MFixLine(p, y, &mc);
  ml = p->w_mlines + y;
# ifdef COLORS16
  if (mc.attr)
    for (x = xs; x <= xe; x++)
      ml->attr[x] = mc.attr;
# endif
  if (mc.color)
    for (x = xs; x <= xe; x++)
      ml->color[x] = mc.color;
# ifdef COLORS256
  if (mc.colorx)
    for (x = xs; x <= xe; x++)
      ml->colorx[x] = mc.colorx;
# endif
}
#endif


#ifdef COPY_PASTE
static void
WAddLineToHist(wp, ml)
struct win *wp;
struct mline *ml;
{
  register unsigned char *q, *o;
  struct mline *hml;

  if (wp->w_histheight == 0)
    return;
  hml = &wp->w_hlines[wp->w_histidx];
  q = ml->image; ml->image = hml->image; hml->image = q;

  q = ml->attr; o = hml->attr; hml->attr = q; ml->attr = null;
  if (o != null)
    free(o);
 
#ifdef FONT
  q = ml->font; o = hml->font; hml->font = q; ml->font = null;
  if (o != null)
    free(o);
  q = ml->fontx; o = hml->fontx; hml->fontx = q; ml->fontx = null;
  if (o != null)
    free(o);
#endif

#ifdef COLOR
  q = ml->color; o = hml->color; hml->color = q; ml->color = null;
  if (o != null)
    free(o);
# ifdef COLORS256
  q = ml->colorx; o = hml->colorx; hml->colorx = q; ml->colorx = null;
  if (o != null)
    free(o);
# endif
#endif

  if (++wp->w_histidx >= wp->w_histheight)
    wp->w_histidx = 0;
  if (wp->w_scrollback_height < wp->w_histheight)
    ++wp->w_scrollback_height;
}
#endif

int
MFindUsedLine(p, ye, ys)
struct win *p;
int ys, ye;
{
  int y;
  struct mline *ml = p->w_mlines + ye;

  debug2("MFindUsedLine: %d %d\n", ye, ys);
  for (y = ye; y >= ys; y--, ml--)
    {
      if (bcmp((char*)ml->image, blank, p->w_width))
	break;
      if (ml->attr != null && bcmp((char*)ml->attr, null, p->w_width))
	break;
#ifdef COLOR
      if (ml->color != null && bcmp((char*)ml->color, null, p->w_width))
	break;
# ifdef COLORS256
      if (ml->colorx != null && bcmp((char*)ml->colorx, null, p->w_width))
	break;
# endif
#endif
#ifdef UTF8
      if (p->w_encoding == UTF8)
	{
	  if (ml->font != null && bcmp((char*)ml->font, null, p->w_width))
	    break;
	  if (ml->fontx != null && bcmp((char*)ml->fontx, null, p->w_width))
	    break;
	}
#endif
    }
  debug1("MFindUsedLine returning  %d\n", y);
  return y;
}


/*
 *====================================================================*
 *====================================================================*
 */

/*
 * Tricky: send only one bell even if the window is displayed
 * more than once.
 */
void
WBell(p, visual)
struct win *p;
int visual;
{
  struct canvas *cv;
  if (displays == NULL)
    p->w_bell = BELL_DONE;
  for (display = displays; display; display = display->d_next)
    {
      for (cv = D_cvlist; cv; cv = cv->c_next)
	if (cv->c_layer->l_bottom == &p->w_layer)
	  break;
      if (cv && !visual)
	AddCStr(D_BL);
      else if (cv && D_VB)
	AddCStr(D_VB);
      else
        p->w_bell = visual ? BELL_VISUAL : BELL_FOUND;
    }
}

/*
 * This should be reverse video.
 * Only change video if window is fore.
 * Because it is used in some termcaps to emulate
 * a visual bell we do this hack here.
 * (screen uses \Eg as special vbell sequence)
 */
static void
WReverseVideo(p, on)
struct win *p;
int on;
{
  struct canvas *cv;
  for (cv = p->w_layer.l_cvlist; cv; cv = cv->c_lnext)
    {
      display = cv->c_display;
      if (cv != D_forecv)
	continue;
      ReverseVideo(on);
      if (!on && p->w_revvid && !D_CVR)
	{
	  if (D_VB)
	    AddCStr(D_VB);
	  else
	    p->w_bell = BELL_VISUAL;
	}
    }
}

void
WMsg(p, err, str)
struct win *p;
int err;
char *str;
{
  extern struct layer *flayer;
  struct layer *oldflayer = flayer;
  flayer = &p->w_layer;
  LMsg(err, "%s", str);
  flayer = oldflayer;
}

void
WChangeSize(p, w, h)
struct win *p;
int w, h;
{
  int wok = 0;
  struct canvas *cv;

  if (p->w_layer.l_cvlist == 0)
    {
      /* window not displayed -> works always */
      ChangeWindowSize(p, w, h, p->w_histheight);
      return;
    }
  for (cv = p->w_layer.l_cvlist; cv; cv = cv->c_lnext)
    {
      display = cv->c_display;
      if (p != D_fore)
	continue;		/* change only fore */
      if (D_CWS)
	break;
      if (D_CZ0 && (w == Z0width || w == Z1width))
	wok = 1;
    }
  if (cv == 0 && wok == 0)	/* can't change any display */
    return;
  if (!D_CWS)
    h = p->w_height;
  ChangeWindowSize(p, w, h, p->w_histheight);
  for (display = displays; display; display = display->d_next)
    {
      if (p == D_fore)
	{
	  if (D_cvlist && D_cvlist->c_next == 0)
	    ResizeDisplay(w, h);
	  else
	    ResizeDisplay(w, D_height);
	  ResizeLayersToCanvases();	/* XXX Hmm ? */
	  continue;
	}
      for (cv = D_cvlist; cv; cv = cv->c_next)
	if (cv->c_layer->l_bottom == &p->w_layer)
	  break;
      if (cv)
	Redisplay(0);
    }
}

static int
WindowChangedCheck(s, what, hp)
char *s;
int what;
int *hp;
{
  int h = 0;
  int l;
  while(*s)
    {
      if (*s++ != (hp ? '%' : '\005'))
	continue;
      l = 0;
      s += (*s == '+');
      s += (*s == '-');
      while (*s >= '0' && *s <= '9')
	s++;
      if (*s == 'L')
	{
	  s++;
	  l = 0x100;
	}
      if (*s == 'h')
	h = 1;
      if (*s == what || ((*s | l) == what) || what == 'd')
	break;
      if (*s)
	s++;
    }
  if (hp)
    *hp = h;
  return *s ? 1 : 0;
}

void
WindowChanged(p, what)
struct win *p;
int what;
{
  int inwstr, inhstr, inlstr;
  int inwstrh = 0, inhstrh = 0, inlstrh = 0;
  int got, ox, oy;
  struct display *olddisplay = display;
  struct canvas *cv;

  inwstr = inhstr = 0;

  if (what == 'f')
    {
      WindowChanged((struct win *)0, 'w'|0x100);
      WindowChanged((struct win *)0, 'W'|0x100);
    }

  if (what)
    {
      inwstr = WindowChangedCheck(captionstring, what, &inwstrh);
      inhstr = WindowChangedCheck(hstatusstring, what, &inhstrh);
      inlstr = WindowChangedCheck(wliststr, what, &inlstrh);
    }
  else
    {
      inwstr = inhstr = 0;
      inlstr = 1;
    }

  if (p == 0)
    {
      for (display = displays; display; display = display->d_next)
	{
	  ox = D_x;
	  oy = D_y;
	  for (cv = D_cvlist; cv; cv = cv->c_next)
	    {
	      if (inlstr || (inlstrh && p && p->w_hstatus && *p->w_hstatus && WindowChangedCheck(p->w_hstatus, what, (int *)0)))
		WListUpdatecv(cv, (struct win *)0);
	      p = Layer2Window(cv->c_layer);
	      if (inwstr || (inwstrh && p && p->w_hstatus && *p->w_hstatus && WindowChangedCheck(p->w_hstatus, what, (int *)0)))
	        if (cv->c_ye + 1 < D_height)
		  RefreshLine(cv->c_ye + 1, 0, D_width - 1, 0);
	    }
	  p = D_fore;
	  if (inhstr || (inhstrh && p && p->w_hstatus && *p->w_hstatus && WindowChangedCheck(p->w_hstatus, what, (int *)0)))
	    RefreshHStatus();
	  if (ox != -1 && oy != -1)
	    GotoPos(ox, oy);
	}
      display = olddisplay;
      return;
    }

  if (p->w_hstatus && *p->w_hstatus && (inwstrh || inhstrh || inlstrh) && WindowChangedCheck(p->w_hstatus, what, (int *)0))
    {
      inwstr |= inwstrh;
      inhstr |= inhstrh;
      inlstr |= inlstrh;
    }
  if (!inwstr && !inhstr && !inlstr)
    return;
  for (display = displays; display; display = display->d_next)
    {
      got = 0;
      ox = D_x;
      oy = D_y;
      for (cv = D_cvlist; cv; cv = cv->c_next)
	{
	  if (inlstr)
	    WListUpdatecv(cv, p);
	  if (Layer2Window(cv->c_layer) != p)
	    continue;
	  got = 1;
	  if (inwstr && cv->c_ye + 1 < D_height)
	    RefreshLine(cv->c_ye + 1, 0, D_width - 1, 0);
	}
      if (got && inhstr && p == D_fore)
	RefreshHStatus();
      if (ox != -1 && oy != -1)
	GotoPos(ox, oy);
    }
  display = olddisplay;
}

