/* Copyright (c) 1991
 *      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 1, 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, write to the
 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Noteworthy contributors to screen's design and implementation:
 *	Wayne Davison (davison@borland.com)
 *	Patrick Wolfe (pat@kai.com, kailand!pat)
 *	Bart Schaefer (schaefer@cse.ogi.edu)
 *	Nathan Glasser (nathan@brokaw.lcs.mit.edu)
 *	Larry W. Virden (lwv27%cas.BITNET@CUNYVM.CUNY.Edu)
 *	Howard Chu (hyc@hanauma.jpl.nasa.gov)
 *	Tim MacKenzie (tym@dibbler.cs.monash.edu.au)
 *	Markku Jarvinen (mta@{cc,cs,ee}.tut.fi)
 *	Marc Boucher (marc@CAM.ORG)
 *
 ****************************************************************
 */

#ifndef lint
  static char rcs_id[] = "$Id: mark.c,v 1.2 92/02/03 02:27:48 jnweiger Exp $ FAU";
#endif

#include <sys/types.h>

#ifdef BSD
# include <sys/signal.h>
#endif /* BSDI */

#include "config.h"
#include "screen.h"
#include "ansi.h"	/* here we find A_SO, ASCII, EXPENSIVE */
#include "extern.h"

static int is_letter __P((int));
static void nextword __P((int *, int *, int, int));
static int linestart __P((int));
static int lineend __P((int));
static int rem __P((int, int , int , int , int , char *, int));
static int eq __P((int, int ));
static void revto __P((int, int));
static void revto_line __P((int, int, int));
static void MarkRedisplayLine __P((int, int, int, int));
static int MarkRewrite __P((int, int, int, int));
static void process_mark_input __P((char **, int *));
static void AbortMarkRoutine __P((void));
static int MarkScrollDownDisplay __P((int));
static int MarkScrollUpDisplay __P((int));

int join_with_cr =  0;
extern struct win *fore, *wtab[];
extern int screenwidth, screenheight;
extern int screentop, screenbot;
extern char GlobalAttr, GlobalCharset;
extern int in_ovl;
extern int HS;
extern int LP;
extern char *null, *blank;

#ifdef NETHACK
extern nethackflag;
#endif

char *copybuffer = NULL;
int copylen = 0;
char mark_key_tab[256]; /* this array must be initialised first! */

static int in_mark;	/* mark routine active */
static int left_mar, right_mar, nonl;
static int x1, y1, second; /* y1 is in terms of WIN coordinates, not DISPLAY */
static int cx, cy;	/* Cursor Position in WIN coords*/
static rep_cnt;		/* no. of repeats are rep_cnt+1. jw. */
static int append_mode;	/* shall we overwrite or append to copybuffer */
static write_buffer;	/* shall we do a KEY_WRITE_EXCHANGE right away? */
static hist_offset;

static int is_letter(c)
char c;
{
  if ((c >= 'a' && c <= 'z') ||
      (c >= 'A' && c <= 'Z') ||
      (c >= '0' && c <= '9') ||
      c == '_' || c == '.' ||
      c == '@' || c == ':' ||
      c == '%' || c == '!' ||
      c == '-' || c == '+')
    /* thus we can catch email-addresses as a word :-) */
    return 1;
  else if (c != ' ')
    return 2;
  return 0;
}

/*
 * iWIN gives us a reference to line y of the *whole* image 
 * where line 0 is the oldest line in our history.
 * y must be in WIN coordinate system, not in display.
 */
#define iWIN(y) ((y < fore->histheight) ? fore->ihist[(fore->histidx + y)\
		% fore->histheight] : fore->image[y - fore->histheight])
#define aWIN(y) ((y < fore->histheight) ? fore->ahist[(fore->histidx + y)\
		% fore->histheight] : fore->attr[y - fore->histheight])
#define fWIN(y) ((y < fore->histheight) ? fore->fhist[(fore->histidx + y)\
		% fore->histheight] : fore->font[y - fore->histheight])
/*
 * hist_offset tells us, how many lines there are on top of the
 * visible screen.
 */

#define W2D(y) ((y)-hist_offset)
#define D2W(y) ((y)+hist_offset)

static int
linestart(y)
int y;
{
  register int x;
  register char *i;

  for (x = left_mar, i = iWIN(y) + x; x < screenwidth-1; x++)
    if (*i++ != ' ')
      break;
  if (x == screenwidth-1)
    x = left_mar;
  return(x);
}

static int
lineend(y)
int y;
{
  register int x;
  register char *i;

  for (x = right_mar, i = iWIN(y) + x; x >= 0; x--)
    if (*i-- != ' ')
      break;
  if (x < 0)
    x = left_mar;
  return(x);
}


/*
 *  nextword calculates the cursor position of the num'th word.
 *  If the cursor is on a word, it counts as the first.
 *  NW_BACK:		search backward
 *  NW_ENDOFWORD:	find the end of the word
 *  NW_MUSTMOVE:	move even if the position is correct.
 */

#define NW_BACK		1
#define NW_ENDOFWORD	2
#define NW_MUSTMOVE	4

static void
nextword(xp, yp, flags, num)
int *xp, *yp, flags, num;
{
  int xx = screenwidth, yy = fore->histheight + screenheight;
  register int sx, oq, q, x, y;

  x = *xp;
  y = *yp;
  sx = (flags & NW_BACK) ? -1 : 1;
  if ((flags & NW_ENDOFWORD) && (flags & NW_MUSTMOVE))
    x += sx;
  for (oq = -1; ; x += sx, oq = q)
    {
      if (x >= xx || x < 0)
	q = 0;
      else
        q = is_letter(iWIN(y)[x]);
      if (oq >= 0 && oq != q)
	{
	  if (oq == 0 || !(flags & NW_ENDOFWORD))
	    *xp = x;
	  else
	    *xp = x-sx;
	  *yp = y;
	  if ((!(flags & NW_ENDOFWORD) && q) ||
	      ((flags & NW_ENDOFWORD) && oq))
	    {
	      if (--num <= 0)
	        return;
	    }
	}
      if (x == xx)
	{
	  x = -1;
	  if (++y >= yy)
	    return;
	}
      else if (x < 0)
	{
	  x = xx;
	  if (--y < 0)
	    return;
	}
    }
}


/*
 * y1, y2 are WIN coordinates
 *
 * redisplay:	0  -  just copy
 * 		1  -  redisplay + copy
 *		2  -  count + copy, don't redisplay
 */

static int rem(x1, y1, x2, y2, redisplay, pt, yend)
int x1, y1, x2, y2, redisplay, yend;
char *pt;
{
  int i, j, from, to, ry;
  int l = 0;
  char *im;

  second = 0;
  if (y2 < y1 || ((y2 == y1) && (x2 < x1)))
    {
      i = y2;
      y2 = y1;
      y1 = i;
      i = x2;
      x2 = x1;
      x1 = i;
    }
  ry = y1 - hist_offset;
  
  i = y1;
  if (redisplay != 2 && pt == 0 && ry <0)
    {
      i -= ry;
      ry = 0;
    }
  for (; i <= y2; i++, ry++)
    {
      if (redisplay != 2 && pt == 0 && ry > yend)
	break;
      from = (i == y1) ? x1 : 0;
      if (from < left_mar)
	from = left_mar;
      for (to = screenwidth-1, im = iWIN(i)+to; to>=0; to--)
        if (*im-- != ' ')
	  break;
      if (i == y2 && x2 < to)
	to = x2;
      if (to > right_mar)
	to = right_mar;
      if (redisplay == 1 && from <= to && ry >=0 && ry <= yend)
	MarkRedisplayLine(ry, from, to, 0);
      if (redisplay != 2 && pt == 0)	/* don't count/copy */
	continue;
      for (j = from, im = iWIN(i)+from; j <= to; j++)
	{
	  if (pt)
	    *pt++ = *im++;
	  l++;
	}
      if (i != y2)
	{
	  /* 
	   * this code defines, what glues lines together
	   */
	  switch (nonl)
	    {
	    case 0:		/* lines separated by newlines */
	      if (join_with_cr)
		{
		  if (pt)
		    *pt++ = '\r';
		  l++;
		}
	      if (pt)
		*pt++ = '\n';
	      l++;
	      break;
	    case 1:		/* nothing to separate lines */
	      break;
	    case 2:		/* lines separated by blanks */
	      if (pt)
		*pt++ = ' ';
	      l++;
	      break;
	    }
	}
    }
  return(l);
}

static int eq(a, b)
int a, b;
{
  if (a == b)
    return 1;
  if (a == 0 || b == 0)
    return 1;
  if (a <= '9' && a >= '0' && b <= '9' && b >= '0')
    return 1;
  return 0;
}

static int crazychar = 0;
static int crazy_y = -1;
static int crazy_x = -1;

int MarkRoutine(flag)	/* return value 1 when copybuffer changed; */
int flag;
{
  int x, y, i;
 
  hist_offset = fore->histheight;
 
  if (!fore->active)
    {
      Msg(0, "Fore window is not active !!!");
      return 0;
    }

  second = 0;
  rep_cnt = 0;
  append_mode = 0;
  write_buffer = 0;
  nonl = left_mar = 0;
  right_mar = screenwidth-1;
  x = fore->x;
  y = D2W(fore->y);
  if (x >= screenwidth)
    x = screenwidth-1;

  if (flag == CRAZY && crazychar != 0 && crazy_x != -1 && crazy_y != -1)
    {
      Msg(0, "CRAZY mode not impl.\n");
    }
  crazychar = 0;
  crazy_y = -1;
  crazy_x = -1;
  if (flag == TRICKY)
    {
      int f, q = 0, xx, yy;
      char *linep;

      debug2("cursor is at x=%d, y=%d\n", fore->x, D2W(fore->y));
      for (xx = fore->x - 1, linep = iWIN(y) + xx; xx >= 0; xx--)
	if ((q = *linep--) != ' ' )
	  break;
      debug3("%c at (%d,%d)\n", q, xx, y);
      for (yy = D2W(fore->y) - 1; yy >= 0; yy--)
	if (xx < 0 || eq(iWIN(yy)[xx], q))
	  {		/* line is matching... */
	    f = 0;
	    for (i = fore->x; i < screenwidth-1; i++)
	      {
		if (iWIN(yy)[i] != ' ')
		  {
		    f = 1;
		    break;
		  }
	      }
	    if (f)
	      break;
	  }
      if (yy < 0)
	return 0;
      xx = 0;
      for (i = screenwidth-1, linep = iWIN(yy)+i; i>0; i--)
	if (*linep-- != ' ')
	  break;
      if (i < x)
	i = x;
      if (copybuffer != NULL)
	Free(copybuffer);
      if ((copybuffer = malloc((unsigned) (i - x + 2))) == NULL)
	{
	  Msg(0, "Not enough memoooh!... Sorry.");
	  return 0;
	}
      rem(x, yy, i, yy, 0, copybuffer, 0);
      copylen = i - x + 1;
      return 1;
    }
  InitOverlayPage(process_mark_input, MarkRedisplayLine, MarkRewrite, 1);
  GotoPos(x, W2D(y));
#ifdef NETHACK
  if (nethackflag)
    Msg(0, "Welcome to hacker's treasure zoo - Column %d Line %d(+%d) (%d,%d)",
	x+1, W2D(y+1), fore->histheight, fore->width, fore->height);
  else
#endif
  Msg(0, "Copy mode - Column %d Line %d(+%d) (%d,%d)",
      x+1, W2D(y+1), fore->histheight, fore->width, fore->height);
  fflush(stdout);
  cx = x1 = x;
  cy = y1 = y;
  in_mark = 1;
  return 0;
}

static void process_mark_input(inbufp,inlenp)
char **inbufp;
int *inlenp;
{
  char *inbuf, *pt;
  int inlen;
  int x2, y2, i, j, yend;
  int newcopylen = 0, od;
/*
  char *extrap = 0, extrabuf[100];
*/
      
  if (inbufp == 0)
    {
      AbortMarkRoutine();
      return;
    }
 
  inbuf= *inbufp;
  inlen= *inlenp;
  pt = inbuf;
  while (in_mark && (inlen /* || extrap */))
    {
      if (!HS)
	RemoveStatus();
/*
      if (extrap)
	{
	  od = *extrap++;
	  if (*extrap == 0)
	    extrap = 0;
	}
      else
*/
	{
          od = mark_key_tab[*pt++];
          inlen--;
	}
      if (od >= '0' && od <= '9')
        {
	  if (rep_cnt < 1001 && (od != '0' || rep_cnt != 0))
	    {
	      rep_cnt = 10 * rep_cnt + od - '0';
	      continue;
 	      /*
	       * Now what is that 1001 here? Well, we have a screen with
	       * 25 * 80 = 2000 characters. Movement is at most across the full
	       * screen. This we do with word by word movement, as character by
	       * character movement never steps over line boundaries. The most words
	       * we can place on the screen are 1000 single letter words. Thus 1001
	       * is sufficient. Users with bigger screens never write in single letter
	       * words, as they should be more advanced. jw.
	       * Oh, wrong. We still give even the experienced user a factor of ten.
	       */
	    }
	}
      switch (od)
	{
	case '\014':	/* CTRL-L Redisplay */
	  Redisplay(0);
	  GotoPos(cx, W2D(cy));
	  break;
	case '\010':	/* CTRL-H Backspace */
	case 'h':
	  if (rep_cnt == 0)
	    rep_cnt = 1;
	  revto(cx - rep_cnt, cy);
	  break;
	case '\016':	/* CTRL-N */
	case 'j':
	  if (rep_cnt == 0)
	    rep_cnt = 1;
	  revto(cx, cy + rep_cnt);
	  break;
	case '+':
	  if (rep_cnt == 0)
	    rep_cnt = 1;
	  j = cy + rep_cnt;
	  if (j > fore->histheight + screenheight - 1)
	    j = fore->histheight + screenheight - 1;
	  revto(linestart(j), j);
	  break;
	case '-':
	  if (rep_cnt == 0)
	    rep_cnt = 1;
	  j = cy - rep_cnt;
	  if (j < 0)
	    j = 0;
	  revto(linestart(j), j);
	  break;
	case '^':
	  revto(linestart(cy), cy);
	  break;
	case '\n':
	  revto(left_mar, cy + 1);
	  break;
	case 'k':
	case '\020':	/* CTRL-P */
	  if (rep_cnt == 0)
	    rep_cnt = 1;
	  revto(cx, cy - rep_cnt);
	  break;
	case 'l':
	  if (rep_cnt == 0)
	    rep_cnt = 1;
	  revto(cx + rep_cnt, cy);
	  break;
	case '\001':	/* CTRL-A from tcsh/emacs */
	case '0':
	  revto(left_mar, cy);
	  break;
	case '\004':    /* CTRL-D down half screen */
	  if (rep_cnt == 0)
	    rep_cnt = (screenheight+1) >> 1;
	  revto_line(cx, cy + rep_cnt, W2D(cy));
	  break;
	case '$':
	  revto(lineend(cy), cy);
	  break;
	case '\025':	/* CTRL-U up half screen */
	  if (rep_cnt == 0)
	    rep_cnt = (screenheight+1) >> 1;
	  revto_line(cx, cy - rep_cnt, W2D(cy));
	  break;
	case '?':
	  if (left_mar == 0 && right_mar == screenwidth - 1)
	    Msg(0, "Column %d Line %d(+%d)", cx+1, W2D(cy)+1,
		hist_offset);
	  else
	    Msg(0, "Column %d(%d..%d) Line %d(+%d)", cx+1,
		left_mar+1, right_mar+1, W2D(cy)+1, hist_offset);
	  break;
	case '\002':	/* CTRL-B  back one page */
	  if (rep_cnt == 0)
	    rep_cnt = 1;
	  rep_cnt *= (screenheight-1);
	  revto(cx, cy - rep_cnt);
	  break;
	case '\006':	/* CTRL-F  forward one page */
	  if (rep_cnt == 0)
	    rep_cnt = 1;
	  rep_cnt *= (screenheight-1);
	  revto(cx, cy + rep_cnt);
	  break;
	case '\005':	/* CTRL-E  scroll up */
	  if (rep_cnt == 0)
	    rep_cnt = 1;
	  rep_cnt = MarkScrollUpDisplay(rep_cnt);
	  if (cy < D2W(0))
            revto(cx, D2W(0));
	  else
            GotoPos(cx, W2D(cy));
	  break;
	case '\031': /* CTRL-Y  scroll down */
	  if (rep_cnt == 0)
	    rep_cnt = 1;
	  rep_cnt = MarkScrollDownDisplay(rep_cnt);
	  if (cy > D2W(screenheight-1))
            revto(cx, D2W(screenheight-1));
	  else
            GotoPos(cx, W2D(cy));
	  break;
	case '@':
	  /* it may be usefull to have a key that does nothing */
	  break;
	case '%':
	  rep_cnt--;
	  /* rep_cnt is a percentage for the history buffer */
	  if (rep_cnt < 0)
	    rep_cnt = 0;
	  if (rep_cnt > 100)
	    rep_cnt = 100;
	  revto_line(left_mar, (rep_cnt * (fore->histheight + screenheight)) / 100, (screenheight-1)/2);
	  break;
	case 'g':
	  rep_cnt = 1;
	  /* FALLTHROUGH */
	case 'G':
	  /* rep_cnt is here the WIN line number */
	  if (rep_cnt == 0)
	    rep_cnt = fore->histheight + screenheight;
	  revto_line(left_mar, --rep_cnt, (screenheight-1)/2);
	  break;
	case 'H':
	  revto(left_mar, D2W(0));
	  break;
	case 'M':
	  revto(left_mar, D2W((screenheight-1) / 2));
	  break;
	case 'L':
	  revto(left_mar, D2W(screenheight-1));
	  break;
	case '|':
	  revto(--rep_cnt, cy);
	  break;
	case 'w':
	  i = cx;
	  j = cy;
	  if (rep_cnt == 0)
	    rep_cnt = 1;
	  nextword(&i, &j, NW_MUSTMOVE, rep_cnt);
	  revto(i, j);
	  break;
	case 'e':
	  i = cx;
	  j = cy;
	  if (rep_cnt == 0)
	    rep_cnt = 1;
	  nextword(&i, &j, NW_ENDOFWORD|NW_MUSTMOVE, rep_cnt);
	  revto(i, j);
	  break;
	case 'b':
	  i = cx;
	  j = cy;
	  if (rep_cnt == 0)
	    rep_cnt = 1;
	  nextword(&i, &j, NW_BACK|NW_ENDOFWORD|NW_MUSTMOVE, rep_cnt);
	  revto(i, j);
	  break;
	case 'a':
	  append_mode = 1 - append_mode;
	  debug1("append mode %d--\n", append_mode);
	  Msg(0, (append_mode) ? ":set append" : ":set noappend");
	  break;
	case 'v':
	case 'V':
	  /* this sets start column to column 9 for VI :set nu users */
	  if (left_mar == 8)
	    rep_cnt = 1;
	  else
	    rep_cnt = 9;
	  /* FALLTHROUGH */
	case 'c':
	case 'C':
	  /* set start column (c) and end column (C) */
	  if (second)
	    {
	      rem(x1, y1, cx, cy, 1, (char *)0, screenheight-1); /* Hack */
	      second = 1;	/* rem turns off second */
	    }
	  rep_cnt--;
	  if (rep_cnt < 0)
	    rep_cnt = cx;
	  if (od != 'C')
	    {
	      left_mar = rep_cnt;
	      if (left_mar > right_mar)
		left_mar = right_mar;
	    }
	  else
	    {
	      right_mar = rep_cnt;
	      if (left_mar > right_mar)
		right_mar = left_mar;
	    }
	  if (second)
	    {
	      int x = cx, y = cy;
	      cx = x1; cy = y1;
	      revto(x, y);
	    }
	  if (od == 'v' || od == 'V')
	    Msg(0, (left_mar != 8) ? ":set nonu" : ":set nu");
	  break;
	case 'J':
	  /* how do you join lines in VI ? */
	  nonl = (nonl + 1) % 3;
	  switch (nonl)
	    {
	    case 0:
	      if (join_with_cr)
		Msg(0, "Multiple lines (CR/LF)");
	      else
		Msg(0, "Multiple lines (LF)");
	      break;
	    case 1:
	      Msg(0, "Lines joined");
	      break;
	    case 2:
	      Msg(0, "Lines joined with blanks");
	      break;
	    }
	  break;
	case 'y':
	case 'Y':
	  if (!second)
	    {
	      revto(linestart(cy), cy);
	      second++;
	      x1 = cx;
	      y1 = cy;
	    }
	  if (--rep_cnt > 0)
	    revto(cx, cy + rep_cnt);
	  revto(lineend(cy), cy);
	  if (od == 'y')
	    break;
	  /* FALLTHROUGH */
	case 'W':
	  if (od == 'W')
	    {
	      if (rep_cnt == 0)
		rep_cnt = 1;
	      if (!second)
		{
		  i = cx;
		  j = cy;
		  nextword(&i, &j, NW_BACK|NW_ENDOFWORD, 1);
		  revto(i, j);
		  second++;
		  x1 = cx;
		  y1 = cy;
		}
	      i = cx;
	      j = cy;
	      nextword(&i, &j, NW_ENDOFWORD, rep_cnt);
	      revto(i, j);
	    }
	  /* FALLTHROUGH */
	case 'A':
	  if (od == 'A')
	    append_mode = 1;
	  /* FALLTHROUGH */
	case '>':
	  if (od == '>')
	    write_buffer = 1;
	  /* FALLTHROUGH */
	case ' ':
	case '\r':
	  if (!second)
	    {
	      second++;
	      x1 = cx;
	      y1 = cy;
	      revto(x1, y1);
#ifdef NETHACK
	      if (nethackflag)
		Msg(0, "You drop a magic marker - Column %d Line %d",
	    	    cx+1, W2D(cy)+1, hist_offset);
	      else
#endif
	      Msg(0, "First mark set - Column %d Line %d", cx+1, cy+1);
	      break;
	    }
	  else
	    {
	      x2 = cx;
	      y2 = cy;
	      newcopylen = rem(x1, y1, x2, y2, 2, (char *)0, 0); /* count */
	      if (copybuffer != NULL && !append_mode)
		{
		  copylen = 0;
		  Free(copybuffer);
		}
	      if (newcopylen > 0)
		{
		  /* the +3 below is for : cr + lf + \0 */
		  if (copybuffer != NULL)
		    copybuffer = realloc(copybuffer,
			(unsigned) (copylen + newcopylen + 3));
		  else
		    {
		    copylen = 0;
		    copybuffer = malloc((unsigned) (newcopylen + 3));
		    }
		  if (copybuffer == NULL)
		    {
		      AbortMarkRoutine();
		      Msg(0, "Not enough memoooh!... Sorry.");
		      copylen = 0;
		      copybuffer = NULL;
		      break;
		    }
		  if (append_mode)
		    {
		      switch (nonl)
			/* 
			 * this code defines, what glues lines together
			 */
			{
			case 0:
			  if (join_with_cr)
			    {
			      copybuffer[copylen] = '\r';
			      copylen++;
			    }
			  copybuffer[copylen] = '\n';
			  copylen++;
			  break;
			case 1:
			  break;
			case 2:
			  copybuffer[copylen] = ' ';
			  copylen++;
			  break;
			}
		    }
		  yend = screenheight - 1;
		  if (fore->histheight - hist_offset < screenheight)
		    {
		      second = 0;
		      yend -= MarkScrollUpDisplay(fore->histheight - hist_offset);
		    }
		  copylen += rem(x1, y1, x2, y2, hist_offset == fore->histheight, copybuffer + copylen, yend);
		}
	      if (hist_offset != fore->histheight)
		{
		  in_ovl = 0;	/* So we can use Activate() */
		  Activate(0);
		}
	      ExitOverlayPage();
	      if (append_mode)
		Msg(0, "Appended %d characters to buffer",
		    newcopylen);
	      else
		Msg(0, "Copied %d characters into buffer", copylen);
	      if (write_buffer)
		WriteFile(DUMP_EXCHANGE);
	      in_mark = 0;
	      break;
	    }
	default:
	  AbortMarkRoutine();
#ifdef NETHACK
	  if (nethackflag)
	    Msg(0, "You escaped the dungeon.");
	  else
#endif
	  Msg(0, "Copy mode aborted");
	  break;
	}
      rep_cnt = 0;
    }
  fflush(stdout);
  *inbufp = pt;
  *inlenp = inlen;
}

static void revto(tx, ty)
int tx, ty;
{
  revto_line(tx, ty, -1);
}

/* tx, ty: WINDOW,  line: DISPLAY */
static void revto_line(tx, ty, line)
int tx, ty, line;
{
  int fx, fy;
  int x, y, t, revst, reven, qq, ff, tt, st, en, ce = 0;
  int ystart = 0, yend = screenheight-1;
  int i, ry;
 
  if (tx < 0)
    tx = 0;
  else if (tx > screenwidth - 1)
    tx = screenwidth -1;
  if (ty < 0)
    ty = 0;
  else if (ty > fore->histheight + screenheight - 1)
    ty = fore->histheight + screenheight - 1;
  
  fx = cx; fy = cy;
  cx = tx; cy = ty;
/*debug2("revto(%d, %d, ", x1, y1);
  debug2("%d, %d, ", fx, fy);
  debug2("%d, %d)\n", tx, ty);*/
 
  /*
   * if we go to a position that is currently offscreen 
   * then scroll the screen
   */
  i = 0;
  if (line >= 0 && line < screenheight)
    i = W2D(ty) - line;
  else if (ty < hist_offset)
    i = ty - hist_offset;
  else if (ty > hist_offset + (screenheight-1))
    i = ty-hist_offset-(screenheight-1);
  if (i > 0)
    yend -= MarkScrollUpDisplay(i);
  else if (i < 0)
    ystart += MarkScrollDownDisplay(-i);

  if (second == 0)
    {
      GotoPos(tx, W2D(cy));
      return;
    }
  
  qq = x1 + y1 * screenwidth;
  ff = fx + fy * screenwidth; /* "from" offset in WIN coords */
  tt = tx + ty * screenwidth; /* "to" offset  in WIN coords*/
 
  if (ff > tt)
    {
      st = tt; en = ff;
      x = tx; y = ty;
    }
  else
    {
      st = ff; en = tt;
      x = fx; y = fy;
    }
  if (st > qq)
    {
      st++;
      x++;
    }
  if (en < qq)
    en--;
  if (tt > qq)
    {
      revst = qq; reven = tt;
    }
  else
    {
      revst = tt; reven = qq;
    }
  ry = y - hist_offset;
  if (ry < ystart)
    {
      y += (ystart - ry);
      x = 0;
      st = y * screenwidth;
      ry = ystart;
    }
  for (t = st; t <= en; t++, x++)
    {
      if (x >= screenwidth)
	{
	  x = 0;
	  y++, ry++;
	}
      if (ry > yend)
	break;
      if (t == st || x == 0)
	{
	  for (ce = screenwidth-1; ce >= 0; ce--)
	    if (iWIN(y)[ce] != ' ')
	      break;
	}
      if (x <= ce && x >= left_mar && x <= right_mar
          && (LP || x < screenwidth-1 || ry < screenbot))
	{
	  GotoPos(x, W2D(y));
	  if (t >= revst && t <= reven)
	    SaveSetAttr(A_SO, ASCII);
	  else
	    SaveSetAttr(aWIN(y)[x], fWIN(y)[x]);
	  PUTCHAR(iWIN(y)[x]);
	}
    }
  GotoPos(tx, W2D(cy));
}

static void AbortMarkRoutine()
{
  int yend, redisp;

  yend = screenheight - 1;
  redisp = second;
  if (fore->histheight - hist_offset < screenheight)
    {
      second = 0;
      yend -= MarkScrollUpDisplay(fore->histheight - hist_offset);
    }
  if (hist_offset != fore->histheight)
    {
      in_ovl = 0;	/* So we can use Activate() */
      Activate(0);	/* to do a complete redisplay */
    }
  else
    {
      rem(x1, y1, cx, cy, redisp, (char *)0, yend);
    }
  ExitOverlayPage();
  in_mark = 0;
}


static void MarkRedisplayLine(y, xs, xe, isblank)
int y; /* NOTE: y is in DISPLAY coords system! */
int xs, xe;
int isblank;
{
  int x, i, rm;
  int sta, sto, cp; /* NOTE: these 3 are in WINDOW coords system */
  char *wi, *wa, *wf, *oldi;
 
  InsertMode(0); /* Not done in DisplayLine() */

  wi = iWIN(D2W(y));
  wa = aWIN(D2W(y));
  wf = fWIN(D2W(y));
  oldi = isblank ? blank : null;
 
  if (second == 0)
    {
      DisplayLine(oldi, null, null, wi, wa, wf, y, xs, xe);
      return;
    }
 
  sta = y1 * screenwidth + x1;
  sto = cy * screenwidth + cx;
  if (sta > sto)
    {
      i=sta; sta=sto; sto=i;
    }
  cp = D2W(y) * screenwidth + xs;
 
  rm = right_mar;
  for (x = screenwidth - 1; x >= 0; x--)
    if (wi[x] != ' ')
      break;
  if (x < rm)
    rm = x;
 
  for (x = xs; x <= xe; x++, cp++)
    if (cp >= sta && x >= left_mar)
      break;
  if (x > xs)
    DisplayLine(oldi, null, null, wi, wa, wf, y, xs, x-1);
  for (; x <= xe; x++, cp++)
    {
      if (cp > sto || x > rm || (!LP && x >= screenwidth-1 && y == screenbot))
	break;
      GotoPos(x, y);
      SaveSetAttr(A_SO, ASCII);
      PUTCHAR(wi[x]);
    }
  if (x<=xe)
    DisplayLine(oldi, null, null, wi, wa, wf, y, x, xe);
}


static int
MarkRewrite(ry, xs, xe, doit)
int ry, xs, xe, doit;
{
  int dx, x, y, st, en, t, rm;
  char *a, *f, *i;

  y = D2W(ry);
  dx = xe - xs;
  if (doit)
    {
      i = iWIN(y) + xs;
      while (dx--)
        PUTCHAR(*i++);
      return(0);
    }
  
  a = aWIN(y) + xs,
  f = fWIN(y) + xs;
  if (second == 0)
    st = en = -1;
  else
    {
      st = y1 * screenwidth + x1;
      en = cy * screenwidth + cx;
      if (st > en)
        {
          t = st; st = en; en = t;
        }
    }
  t = y * screenwidth + xs;
  for (rm=screenwidth-1, i=iWIN(y) + screenwidth-1; rm>=0; rm--)
    if (*i-- != ' ')
      break;
  if (rm > right_mar)
    rm = right_mar;
  x = xs;
  while (dx--)
    {
      if (t >= st && t <= en && x >= left_mar && x <= rm)
        {
	  if (GlobalAttr != A_SO || GlobalCharset != ASCII)
	    return(EXPENSIVE);
        }
      else
        {
	  if (GlobalAttr != *a || GlobalCharset != *f)
	    return(EXPENSIVE);
        }
      a++, f++, t++, x++;
    }
  return(xe - xs);
}


/*
 * scroll the screen contents up/down.
 */
static int MarkScrollUpDisplay(n)
int n;
{
  int i;
 
  debug1("MarkScrollUpDisplay(%d)\n", n);
  if (n <= 0)
    return 0;
  if (n > fore->histheight - hist_offset)
    n = fore->histheight - hist_offset;
  i = (n < screenheight) ? n : (screenheight);
  ScrollRegion(0, screenheight - 1, i);
  hist_offset += n;
  while (i-- > 0)
    MarkRedisplayLine(screenheight-i-1, 0, screenwidth-1, 1);
  return n;
}

static int MarkScrollDownDisplay(n)
int n;
{
  int i;
 
  debug1("MarkScrollDownDisplay(%d)\n", n);
  if (n <= 0)
    return 0;
  if (n > hist_offset)
    n = hist_offset;
  i = (n < screenheight) ? n : (screenheight);
  ScrollRegion(0, screenheight - 1, -i);
  hist_offset -= n;
  while (i-- > 0)
    MarkRedisplayLine(i, 0, screenwidth-1, 1);
  return n;
}

