/*======================================================================*\
|*		Editor mined						*|
|*		Paste buffer handling					*|
\*======================================================================*/

#include "mined.h"
#include "io.h"	/* for flush, set_cursor */

#include <errno.h>


/*======================================================================*\
|*			System file link behaviour			*|
\*======================================================================*/

#ifdef unix
#define linkyank
#endif

#ifdef __pcgcc__
#undef linkyank
#endif
#ifdef __CYGWIN__
#define linkyank
#endif


/*======================================================================*\
|*		Global variables					*|
\*======================================================================*/

FLAG yank_status = NOT_VALID;	/* Status of yank_file */
static FLAG html_status = NOT_VALID;	/* Status of html_file */
static int yank_buf_no = 0;	/* Buffer # for trials and multiple buffers */
static int max_yank_buf_no = 0;	/* Max Buffer # used */

/* pasted area markers for "Paste previous" function */
static LINE * pasted_start_line = NIL_LINE;
static char * pasted_start_textp = NIL_PTR;
static LINE * pasted_end_line = NIL_LINE;
static char * pasted_end_textp = NIL_PTR;

int buffer_open_flag = 0;	/* Counter flag for the collective buffer */


/*======================================================================*\
|*			Marker data					*|
\*======================================================================*/

/* default marker */
static LINE * mark_line = NIL_LINE;		/* For marking position. */
static char * mark_text = NIL_PTR;

/* explicit markers */
#define maxmarkers 10
static LINE * mark_n_line [maxmarkers] = {
	NIL_LINE, NIL_LINE, NIL_LINE, NIL_LINE, NIL_LINE, 
	NIL_LINE, NIL_LINE, NIL_LINE, NIL_LINE, NIL_LINE};
static char * mark_n_text [maxmarkers] = {
	NIL_PTR, NIL_PTR, NIL_PTR, NIL_PTR, NIL_PTR, 
	NIL_PTR, NIL_PTR, NIL_PTR, NIL_PTR, NIL_PTR};

/* implicit marker stack */
#define markstacklen 10
static struct {
	LINE * line;
	char * text;
	char * file;
	int lineno;
	int pos;
} mark_stack [markstacklen] = {
	{NIL_LINE, NIL_PTR, NIL_PTR, -1, -1},
	{NIL_LINE, NIL_PTR, NIL_PTR, -1, -1},
	{NIL_LINE, NIL_PTR, NIL_PTR, -1, -1},
	{NIL_LINE, NIL_PTR, NIL_PTR, -1, -1},
	{NIL_LINE, NIL_PTR, NIL_PTR, -1, -1},
	{NIL_LINE, NIL_PTR, NIL_PTR, -1, -1},
	{NIL_LINE, NIL_PTR, NIL_PTR, -1, -1},
	{NIL_LINE, NIL_PTR, NIL_PTR, -1, -1},
	{NIL_LINE, NIL_PTR, NIL_PTR, -1, -1},
	{NIL_LINE, NIL_PTR, NIL_PTR, -1, -1},
};
static int mark_stack_poi = 0;
static int mark_stack_top = 0;
static int mark_stack_begin = 0;
static int mark_stack_count = 0;

#define dont_debug_mark_stack

#ifdef debug_mark_stack
#define printf_mark_stack(s)	printf ("%s - mark_stack %d [%d..%d] @ %d\n", s, mark_stack_count, mark_stack_begin, mark_stack_top, mark_stack_poi)
#define printf_debug_mark_stack(s)	printf (s)
#else
#define printf_mark_stack(s)	
#define printf_debug_mark_stack(s)	
#endif


/*======================================================================*\
|*			Basic Paste operations				*|
\*======================================================================*/

/*
 * Legal () checks if mark_text is still a valid pointer.
 */
static
int
legal (mark_line, mark_text)
  register LINE * mark_line;
  register char * mark_text;
{
  register char * textp = mark_line->text;

/* Locate mark_text on mark_line */
  while (textp != mark_text && * textp != '\0') {
	textp ++;
  }
  return (* textp == '\0') ? ERRORS : FINE;
}

/*
 * Check_mark () checks if mark_line and mark_text are still valid pointers.
 * If they are it returns
 * SMALLER if the marked position is before the current,
 * BIGGER if it isn't or SAME if somebody didn't get the point.
 * NOT_VALID is returned when mark_line and/or mark_text are no longer valid.
 * Legal () checks if mark_text is valid on the mark_line.
 */
static
FLAG
checkmark (mark_line, mark_text)
  register LINE * mark_line;
  register char * mark_text;
{
  register LINE * line;
  FLAG cur_seen = False;

/* Special case: check is mark_line and cur_line are the same. */
  if (mark_line == cur_line) {
	if (mark_text == cur_text) {
		/* Even same place */
		return SAME;
	}
	if (legal (mark_line, mark_text) == ERRORS) {
		/* mark_text out of range */
		return NOT_VALID;
	}
	if (mark_text < cur_text) {
		return SMALLER;
	} else {
		return BIGGER;
	}
  }

/* Start looking for mark_line in the line structure */
  for (line = header->next; line != tail; line = line->next) {
	if (line == cur_line) {
		cur_seen = True;
	} else if (line == mark_line) {
		break;
	}
  }

/* If we found mark_line (line != tail) check for legality of mark_text */
  if (line == tail || legal (mark_line, mark_text) == ERRORS) {
	return NOT_VALID;
  }

/* cur_seen is True if cur_line is before mark_line */
  if (cur_seen) {
	return BIGGER;
  } else {
	return SMALLER;
  }
}


/*======================================================================*\
|*		Cumulative buffer handling				*|
\*======================================================================*/

static
void
set_buffer_open (appending)
  FLAG appending;
{
#ifdef debug_ring_buffer
  printf ("set_buffer_open %d\n", buffer_open_flag);
#endif
  if (buffer_open_flag == 0 && (appending == False || yank_buf_no == 0)) {
	yank_buf_no ++;
	if (yank_buf_no > max_yank_buf_no) {
		max_yank_buf_no = yank_buf_no;
	}
	yank_status = NOT_VALID;
#ifdef debug_ring_buffer
	flags_changed = True;
#endif
  }
  buffer_open_flag = 2;
}

static
void
close_buffer ()
{
#ifdef debug_ring_buffer
  if (buffer_open_flag > 0) {
	flags_changed = True;
  }
#endif
  buffer_open_flag = 0;
}

static
void
revert_yank_buf ()
{
  yank_buf_no --;
  if (yank_buf_no <= 0) {
	yank_buf_no = max_yank_buf_no;
  }
}


/*======================================================================*\
|*			Yank text handling				*|
\*======================================================================*/

/*
 * Yank puts all the text between start_position and end_position into
 * the buffer.
 * The caller must check that the arguments to yank_text () are valid (e.g. in
 * the right order).
 */
static
void
yank_text (fd, buf_status, 
	start_line, start_textp, end_line, end_textp, 
	remove, append)
  int fd;
  FLAG * buf_status;
  LINE * start_line;
  char * start_textp;
  LINE * end_line;
  char * end_textp;
  FLAG remove;	/* == DELETE if text should be deleted */
  FLAG append;	/* == True if text should only be appended to yank buffer */
{
  LINE * line = start_line;
  char * textp = start_textp;
  long chars_written = 0L;	/* chars written to buffer this time */
  long bytes_written = 0L;	/* bytes written to buffer this time */
  int lines_written = 0;	/* lines written to buffer this time */
  int return_len;


/* Check file to hold buffer */
  if (fd == ERRORS) {
	return;
  }

  if (append) {
	status_msg ("Appending text ...");
  } else {
	status_msg ("Saving text ...");
	chars_saved = 0L;
	bytes_saved = 0L;
	lines_saved = 0;
  }

/* Keep writing chars until the end_location is reached. */
  chars_written = char_count (textp) - 1;
  while (textp != end_textp) {
	if (* textp == '\n') {
		if (line == end_line) {
			error ("Internal error: passed end of text to be copied");
			(void) close (fd);
			return;
		}

		/* handle different line ends */
		return_len = write_lineend (fd, line->return_type, False);
		if (return_len == ERRORS) {
			error2 ("Write to buffer failed: ", serror ());
			(void) close (fd);
			return;
		}

		lines_written ++;
		if (line->return_type != lineend_NONE) {
			chars_written ++;
		}
		bytes_written += return_len;

		/* move to the next line */
		line = line->next;
		textp = line->text;

		chars_written += char_count (textp) - 1;
	} else {
		if (pastebuf_utf8 && ! utf8_text) {	/* write UTF-8 */
			unsigned long unichar = charvalue (textp);
			character unibuf [13];
			char * up = (char *) unibuf;

			if (cjk_text || mapped_text) {
				unichar = lookup_encodedchar (unichar);
				if (no_unichar (unichar)) {
					unichar = '';
				}
			}

			if (unichar >= 0x80000000) {
				/* special encoding of 2 Unicode chars, 
				   mapped from 1 CJK character */
				up += utfencode (unichar & 0xFFFF, up);
				unichar = (unichar >> 16) & 0x7FFF;
			}

			(void) utfencode (unichar, up);

			/* don't use write_line which might write UTF-16 ! */
			up = (char *) unibuf;
			while (* up != '\0') {
				if (writechar (fd, * up) == ERRORS) {
					error2 ("Write to buffer failed: ", serror ());
					(void) close (fd);
					return;
				}
				up ++;
				bytes_written ++;
			}

			/* move to the next character */
			advance_char (& textp);
		} else {	/* write bytes transparently */
			if (writechar (fd, * textp) == ERRORS) {
				error2 ("Write to buffer failed: ", serror ());
				(void) close (fd);
				return;
			}

			bytes_written ++;

			/* move to the next byte */
			textp ++;
		}
	}
  }

  chars_written -= char_count (end_textp) - 1;

/* Flush the I/O buffer and close file */
  if (flush_filebuf (fd) == ERRORS) {
	error2 ("Write to buffer failed: ", serror ());
	(void) close (fd);
	return;
  }
  if (close (fd) < 0) {
	error2 ("Write to buffer failed: ", serror ());
	return;
  }
  * buf_status = VALID;


  /*
   * Check if the text should be deleted as well. In case it should,
   * the following hack is used to save a lot of code.
   * First move back to the start_position (this might be the current
   * location) and then delete the text.
   * This might look a bit confusing to the user the first time.
   * Delete () will fix the screen.
   */
  if (remove == DELETE) {
	move_to (find_x (start_line, start_textp), find_y (start_line));
	if (delete_text (start_line, start_textp, end_line, end_textp)
		== ERRORS) {
		sleep (2) /* give time to read allocation error msg */;
	}
	mark_line = cur_line;
	mark_text = cur_text;
  }

  bytes_saved += bytes_written;
  chars_saved += chars_written;
  lines_saved += lines_written;

  build_string (text_buffer, "%s paste buffer: lines:chars(bytes) %d:%ld(%ld) - Paste with %s/Insert", 
	(remove == DELETE) ?
		append ? "Cut/appended" : "Cut/moved"
		: append ? "Appended" : "Copied",
	lines_written,
	chars_written,
	bytes_written,
	emulation == 'e' ? "^Y"		/* emacs yank */
	: emulation == 'w' ? "^K^C"	/* WordStart block copy */
	: emulation == 'p' ? "^U"	/* pico uncut */
	: "^P"				/* mined paste */
	);
  status_uni (text_buffer);
}


void
delete_text_buf (start_line, start_textp, end_line, end_textp)
  LINE * start_line;
  char * start_textp;
  LINE * end_line;
  char * end_textp;
{
  if (emacs_buffer) {
	set_buffer_open (False);
	yank_text (yankfile (WRITE, True), & yank_status, 
			start_line, start_textp, end_line, end_textp, 
			DELETE, True);
  } else {
	(void) delete_text (start_line, start_textp, end_line, end_textp);
  }
}


/*======================================================================*\
|*			Yank file reading				*|
\*======================================================================*/

/**
   paste_line calls get_line and converts from Unicode if desired
 */
static
int
paste_line (fd, buffer, len)
  int fd;
  register char buffer [MAX_CHARS];
  int * len;
{
  int ret = get_line (fd, buffer, len, False);

  if (ret == ERRORS) {
	return ret;
  }

  if (utf8_text || ! pastebuf_utf8) {
	return ret;
  } else {
	char nativebuf [2 * MAX_CHARS];
	char * poi = buffer;
	char * npoi = nativebuf;
	unsigned long prev_uc = 0;
	char * prev_npoi;

	while (* poi) {
		int ulen = UTF8_len (* poi);
		unsigned long uc = utf8value (poi);
		char * ppoi = poi;

		advance_utf8 (& poi);
		if (ppoi + ulen != poi) {
			/* illegal UTF-8 value */
			* npoi ++ = '';
			prev_uc = 0;
		} else if (cjk_text || mapped_text) {
			unsigned long nc = encodedchar2 (prev_uc, uc);
			if (no_char (nc)) {
				nc = encodedchar (uc);
			} else {
				npoi = prev_npoi;
			}

			prev_uc = uc;
			prev_npoi = npoi;

			if (no_char (nc)) {
				/* character not known in current encoding */
				* npoi ++ = '';
			} else if (cjk_text) {
				int cjklen = cjkencode (nc, npoi);
				npoi += cjklen;
			} else {
				* npoi ++ = (character) nc;
			}
		} else {
			if (uc >= 0x100) {
				/* character not known in current encoding */
				* npoi ++ = '';
			} else {
				* npoi ++ = (character) uc;
			}
		}
	}
	* npoi = '\0';

	* len = strlen (nativebuf);
	if (* len >= MAX_CHARS) {
		error ("Line too long in current encoding");
		return ERRORS;
	} else {
		strcpy (buffer, nativebuf);
		return ret;
	}
  }
}

/*
 * insert_file () inserts the contents of an opened file (as given by
 * filedescriptor fd) at the current location.
 * After the insertion, if stay_old_pos is True, the cursor remains at the
 * start of the inserted text, if stay_old_pos is False, it is placed to
 * its end.
 */
static
void
insert_file (fd, stay_old_pos, from_text_file)
  int fd;
  FLAG stay_old_pos;
  FLAG from_text_file;	/* consider UTF-16 ? */
{
  char line_buffer [MAX_CHARS];		/* Buffer for next line */
  register LINE * line = cur_line;
  register int line_count = total_lines;	/* Nr of lines inserted */
  LINE * page = cur_line;
  int ret;
  int len;
  lineend_type return_type;

  reset_get_line (from_text_file);

/* Get the first piece of text (might be ended with a '\n') from fd */
  ret = paste_line (fd, line_buffer, & len);
  if (ret == ERRORS) {
	/* empty file */
	return;
  }

/* Adjust line end type if present */
  if (ret == SPLIT_LINE) {
	return_type = lineend_NONE;
  } else if (ret == NUL_LINE) {
	return_type = lineend_NUL;
  } else if (ret != NO_LINE) {
	return_type = extract_lineend_type (line_buffer, len);
  } else {
	return_type = cur_line->return_type;
  }

/* Insert this text at the current location */
  len = cur_text - cur_line->text;
  if (insert_text (line, cur_text, line_buffer) == ERRORS) {
	pasted_end_line = NIL_LINE;
	return;
  }
  cur_line->return_type = return_type;

  pasted_end_line = line;
  pasted_end_textp = line->text + len + length_of (line_buffer);


/* Repeat getting lines (and inserting lines) until EOF is reached */
  while (line != NIL_LINE
	 && (ret = paste_line (fd, line_buffer, & len)) != ERRORS && ret != NO_LINE)
  {
	if (ret == SPLIT_LINE) {
		return_type = lineend_NONE;
	} else if (ret == NUL_LINE) {
		return_type = lineend_NUL;
	} else {
		return_type = extract_lineend_type (line_buffer, len);
	}
	line = line_insert (line, line_buffer, len, return_type);
  }

/* Calculate nr of lines added */
  line_count = total_lines - line_count;

  if (line == NIL_LINE) {
	pasted_end_line = NIL_LINE;
	/* show memory allocation error msg */
	sleep (2);
  } else if (ret == NO_LINE) {	/* Last line read not ended by a '\n' */
	line = line->next;
	if (insert_text (line, line->text, line_buffer) == ERRORS) {
		pasted_end_line = NIL_LINE;
		/* give time to read error msg */
		sleep (2);
	} else {
		pasted_end_line = line;
		pasted_end_textp = line->text + length_of (line_buffer);
	}
  } else if (line_count > 0) {
	pasted_end_line = line->next;
	pasted_end_textp = line->next->text;
  }

  (void) close (fd);

/* If illegal lines were input, report */
  show_get_l_errors ();

/* Fix the screen */
  if (line_count == 0) {		/* Only one line changed */
	print_line (y, line);

	move_to (x, y);
	pasted_start_line = cur_line;
	pasted_start_textp = cur_text;
	if (stay_old_pos == False) {
		move_address (pasted_end_textp, y);
	}
  } else {				/* Several lines changed */
	reset (top_line, y);	/* Reset pointers */
	while (page != line && page != bot_line->next) {
		page = page->next;
	}
	if (page != bot_line->next || stay_old_pos) {
		display (y, cur_line, SCREENMAX - y, y);
		/* screen display style parameter (last) may be inaccurate */
	}

	move_to (x, y);
	pasted_start_line = cur_line;
	pasted_start_textp = cur_text;
	if (stay_old_pos == False) {
		if (ret == NO_LINE) {
			move_address (pasted_end_textp, find_y (line));
		} else {
			move_to (0, find_y (line->next));
		}
	}
  }

/* If number of added lines >= REPORT_CHANGED_LINES, print the count */
  if (line_count >= REPORT_CHANGED_LINES) {
	status_line (dec_out ((long) line_count), " lines added");
  }
}


/**
   Insert the buffer at the current location.
   PASTE () moves the cursor behind the inserted text.
   PASTEstay () moves the cursor in front of the inserted text.
 */
static
void
paste_buffer (old_pos)
  FLAG old_pos;
{
  register int fd;		/* File descriptor for buffer */

  if (viewonly) {
	viewonlyerr ();
	return;
  }

  if (hop_flag > 0) {
	if ((fd = open (yankie_file, O_RDONLY | O_BINARY, 0)) < 0) {
		error ("No inter window buffer present");
		return;
	}
  } else {
	if ((fd = yankfile (READ, False)) == ERRORS) {
		int e = geterrno ();
		if (e == 0 || e == ENOENT /* cygwin */) {
			status_uni ("Buffer is empty - type F1 k for help on copy/paste");
		} else {
			error2 ("Cannot read paste buffer: ", serror ());
		}
		return;
	}
	if (append_flag) {
		close_buffer ();
	}
  }
  /* Insert the buffer */
  insert_file (fd, old_pos, False);
}


/*======================================================================*\
|*			Paste buffer setup				*|
\*======================================================================*/

int
yankfile (mode, append)
  FLAG mode;	/* Can be READ or WRITE permission */
  FLAG append;	/* == True if text should only be appended to yank buffer */
{
  return scratchfile (mode, append, yank_file, "buf", & yank_status);
}

static
int
htmlfile (mode, append)
  FLAG mode;	/* Can be READ or WRITE permission */
  FLAG append;	/* == True if text should only be appended to yank buffer */
{
  return scratchfile (mode, append, html_file, "tag", & html_status);
}


/*======================================================================*\
|*			Copy/Paste and Marker handling			*|
\*======================================================================*/

void
PASTEEXT ()
{
  hop_flag = 1;
  PASTE ();
}

void
PASTE ()
{
  paste_buffer (paste_stay_left);
}

void
PASTEstay ()
{
  paste_buffer (True);
}

void
YANKRING ()
{
  if (cur_line == pasted_end_line && cur_text == pasted_end_textp 
   && checkmark (pasted_start_line, pasted_start_textp) == SMALLER)
  {
	move_address (pasted_start_textp, find_y (pasted_start_line));
	if (delete_text (pasted_start_line, pasted_start_textp, pasted_end_line, pasted_end_textp)
		== ERRORS) {
		sleep (2) /* give time to read allocation error msg */;
	} else {
		/* for some mysterious reason, 
		   this is needed to fix the display: */
		clear_status ();

		revert_yank_buf ();
		paste_buffer (False);
	}
  } else if (cur_line == pasted_start_line && cur_text == pasted_start_textp 
   && checkmark (pasted_start_line, pasted_start_textp) == SAME)
  {
	if (delete_text (pasted_start_line, pasted_start_textp, pasted_end_line, pasted_end_textp)
		== ERRORS) {
		sleep (2) /* give time to read allocation error msg */;
	} else {
		/* for some mysterious reason, 
		   this is needed to fix the display: */
		clear_status ();

		revert_yank_buf ();
		paste_buffer (True);
	}
  } else {
	error ("No previous paste to exchange");
  }
}

/*
 * paste_HTML () inserts the HTML embedding buffer at the current location.
 */
void
paste_HTML ()
{
  int fd;		/* File descriptor for buffer */

  if (viewonly) {
	viewonlyerr ();
	return;
  }

  if ((fd = open (html_file, O_RDONLY | O_BINARY, 0)) < 0) {
	error ("HTML paste buffer vanished");
	return;
  }
  insert_file (fd, False, False);
}

/*
 * INSFILE () prompts for a filename and inserts the file at the current
 * location in the file.
 */
void
INSFILE ()
{
  register int fd;		/* File descriptor of file */
  char name [maxLINE_LEN];	/* Buffer for file name */

  if (restricted) {
	restrictederr ();
	return;
  }

  if (viewonly) {
	viewonlyerr ();
	return;
  }

/* Get the file name */
  if (get_filename ("Get and insert file:", name) != FINE) {
	return;
  }
  clear_status ();

  status_line ("Inserting ", name);
  if ((fd = open (name, O_RDONLY | O_BINARY, 0)) < 0) {
	error2 ("Cannot open file: " /*, name */, serror ());
  } else {	/* Insert the file */
	insert_file (fd, True, True);	/* leave cursor at begin of insertion */
  }
}

/*
 * WB () writes the buffer (yank_file) into another file, which
 * is prompted for.
 */
void
WB ()
{
  register int new_fd;		/* Filedescriptor to copy file */
  int yank_fd;			/* Filedescriptor to buffer */
  register int cnt;		/* Count check for read/write */
  int ret = FINE;		/* Error check for write */
  char wfile_name [maxLINE_LEN];	/* Output file name */
  char * msg_doing; char * msg_done;

  if (restricted) {
	restrictederr ();
	return;
  }

/* Checkout the buffer */
  if ((yank_fd = yankfile (READ, False)) == ERRORS) {
	int e = geterrno ();
	if (e == 0 || e == ENOENT /* cygwin */) {
		error ("Paste buffer is empty");
	} else {
		error2 ("Cannot read paste buffer: ", serror ());
	}
	return;
  }

/* Get file name */
  if (get_filename ((hop_flag > 0) ? "Append buffer to file:"
			       : "Write buffer to file (use with HOP to append):", wfile_name) != FINE)
  {
	return;
  }

/* Create the new file or open previous file for appending */
  if (hop_flag > 0) {
    status_line ("Opening ", wfile_name);
    if ((new_fd = open (wfile_name, O_WRONLY | O_CREAT | O_APPEND | O_BINARY, fprot0)) < 0) {
	error2 ("Cannot append to file: " /* , wfile_name */, serror ());
	return;
    }
    msg_doing = "Appending buffer to ";
    msg_done = "Appended buffer to";
  } else {
    if (checkoverwrite (wfile_name) != True) {
	return;
    } else {
	status_line ("Opening ", wfile_name);
	if ((new_fd = open (wfile_name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, fprot0)) < 0) {
		error2 ("Cannot create file: " /* , wfile_name */, serror ());
		return;
	}
    }
    msg_doing = "Writing buffer to ";
    msg_done = "Wrote buffer to";
  }

  status_line (msg_doing, wfile_name);
  flush ();

/* Copy buffer into file */
  while ((cnt = read (yank_fd, text_buffer, sizeof (text_buffer))) > 0) {
	if (write (new_fd, text_buffer, (unsigned int) cnt) != cnt) {
		error2 ("Writing buffer to file failed: ", serror ());
		ret = ERRORS;
		break;
	}
  }
  if (cnt < 0) {
	error2 ("Reading paste buffer failed: ", serror ());
	ret = ERRORS;
  }

/* Clean up open files and status line */
  (void) close (yank_fd);
  if (close (new_fd) < 0) {
	if (ret != ERRORS) {
		error2 ("Writing buffer to file failed: ", serror ());
		ret = ERRORS;
	}
  }

  if (ret != ERRORS) {
	file_status (msg_done, bytes_saved, chars_saved, 
			wfile_name, lines_saved, 
			False, True, False, False);
  }
}

/*
 * MARK sets mark_line / mark_text to the current line / current text pointer.
 */
void
MARK ()
{
  if (hop_flag > 0) {
	GOMA ();
  } else {
	mark_line = cur_line;
	mark_text = cur_text;
	status_uni ("Mark set - type F1 k for help on copy/paste");
  }
}

/*
 * toggleMARK sets / unsets mark (for pico mode).
 */
void
toggleMARK ()
{
  if (checkmark (mark_line, mark_text) == NOT_VALID) {
	MARK ();
  } else {
	mark_line = NIL_LINE;
	mark_text = NIL_PTR;
	status_msg ("Mark unset");
  }
}

/*
 * GOMA moves to the marked position
 */
void
GOMA ()
{
  if (checkmark (mark_line, mark_text) == NOT_VALID) {
	error ("Mark not set");
  } else {
	Pushmark ();

	move_address (mark_text, find_y (mark_line));
  }
}

/*
 * MARKn sets mark n to the current line / current text pointer.
 * Markn sets it silently.
 */
void
MARKn (n)
  int n;
{
  if (hop_flag > 0) {
	GOMAn (n);
  } else {
	if (n < 0 || n >= maxmarkers) {
		error ("Marker # out of range");
		return;
	}
	mark_n_line [n] = cur_line;
	mark_n_text [n] = cur_text;
	status_msg ("Marker set");
  }
}

void
Markn (n)
  int n;
{
	if (n == -1) {	/* initial mark */
		mark_line = cur_line;
		mark_text = cur_text;
	} else if (n < 0 || n >= maxmarkers) {
		error ("Marker # out of range");
		return;
	} else {
		mark_n_line [n] = cur_line;
		mark_n_text [n] = cur_text;
	}
}

/*
 * GOMAn moves to the marked position n
 */
void
GOMAn (n)
  int n;
{
  Pushmark ();

  if (n < 0 || n >= maxmarkers) {
	error ("Marker # out of range");
	return;
  }

  if (checkmark (mark_n_line [n], mark_n_text [n]) == NOT_VALID) {
	error ("Marker not set");
  } else {
	move_address (mark_n_text [n], find_y (mark_n_line [n]));
  }
}


static
char *
copied_file_name ()
{
  int i;
  char * dup;
  char * filei;

  /* check if file name already in stack */
  for (i = 0; i < markstacklen; i ++) {
	filei = mark_stack [i].file;
	if (filei != NIL_PTR && streq (filei, file_name)) {
		return filei;
	}
  }

  /* make a new copy of file name string */
  dup = alloc (strlen (file_name) + 1);
  if (dup != NIL_PTR) {
	strcpy (dup, file_name);
  }
  return dup;
}

/*
   Pushmark pushes the current position to the mark stack.
 */
void
Pushmark ()
{
	mark_stack [mark_stack_top].line = cur_line;
	mark_stack [mark_stack_top].text = cur_text;
	mark_stack [mark_stack_top].file = copied_file_name ();
	mark_stack [mark_stack_top].lineno = line_number;
	mark_stack [mark_stack_top].pos = get_cur_pos ();

	mark_stack_top = (mark_stack_top + 1) % markstacklen;
	if (mark_stack_top == mark_stack_begin) {
		mark_stack_begin = (mark_stack_begin + 1) % markstacklen;
	} else {
		mark_stack_count ++;
	}
	mark_stack_poi = mark_stack_top;
	printf_mark_stack ("Push");
}

/*
   Popmark pops the current position from the mark stack.
 */
void
Popmark ()
{
	FLAG switch_files;

	if (hop_flag > 0) {
		/* climb up stack towards top */
		printf_mark_stack ("HOP Pop");
		if (mark_stack_count == 0
		    || (mark_stack_poi % markstacklen) == mark_stack_top
		    || ((mark_stack_poi + 1) % markstacklen) == mark_stack_top
		   )
		{
			printf_debug_mark_stack ("HOP Pop no more\n");
			error ("No more stacked positions");
			return;
		}
		mark_stack_poi = (mark_stack_poi + 1) % markstacklen;
		printf_mark_stack ("...");
	} else {
		/* climb down stack towards bottom */
		if (mark_stack_poi == mark_stack_begin) {
			printf_debug_mark_stack ("Pop no more\n");
			error ("No more stacked positions");
			return;
		}
		if (mark_stack_poi == mark_stack_top) {
			/* at top, push current position first */
			printf_mark_stack ("Pop Push");
			Pushmark ();
			mark_stack_poi --;
		}
		mark_stack_poi --;
		if (mark_stack_poi < 0) {
			mark_stack_poi = markstacklen - 1;
		}
	}

	if (mark_stack [mark_stack_poi].file == NIL_PTR) {
		printf_debug_mark_stack ("not valid\n");
		error ("Stacked position not valid");
		return;
	}
	switch_files = ! streq (mark_stack [mark_stack_poi].file, file_name);
	if (switch_files ||
		checkmark (mark_stack [mark_stack_poi].line, 
			mark_stack [mark_stack_poi].text) == NOT_VALID)
	{
		int mark_lineno;
		LINE * open_line;

		if (switch_files) {
			if (save_text_load_file (mark_stack [mark_stack_poi].file) == ERRORS) {
				return;
			}
		}

		mark_lineno = mark_stack [mark_stack_poi].lineno - 1;
		open_line = proceed (header->next, mark_lineno);
		if (open_line == tail) {
			EFILE ();
			error ("Stacked position not present anymore");
		} else {
			int mark_col = mark_stack [mark_stack_poi].pos;
			int cur_pos = 0;
			char * cpoi;

			move_to (0, find_y (open_line));
			cpoi = cur_line->text;
			while (* cpoi != '\n' && cur_pos < mark_col) {
				advance_char (& cpoi);
				cur_pos ++;
			}
			move_address (cpoi, y);
		}
	} else {
		move_address (mark_stack [mark_stack_poi].text, 
				find_y (mark_stack [mark_stack_poi].line));
	}
}


#ifndef linkyank
/*
 * copy one file to the other
   Return False if that fails.
 */
static
FLAG
copyfile (yank_file, yankie_file)
  char * yank_file;
  char * yankie_file;
{
  int yank_fd;	/* Filedescriptor to yank buffer */
  int new_fd;	/* Filedescriptor to yankie file */
  int cnt;	/* Count check for read/write */
  int ret = True;

/* Checkout the buffer */
  if ((yank_fd = open (yank_file, O_RDONLY | O_BINARY, 0)) < 0) {
	return False;
  }

/* Create yankie file */
  if ((new_fd = open (yankie_file, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, bufprot)) < 0) {
	return False;
  }

/* Copy buffer into file */
  while ((cnt = read (yank_fd, text_buffer, sizeof (text_buffer))) > 0) {
	if (write (new_fd, text_buffer, (unsigned int) cnt) != cnt) {
		ret = False;
		break;
	}
  }
  if (cnt < 0) {
	ret = False;
  }

/* Clean up open files and status line */
  (void) close (yank_fd);
  if (close (new_fd) < 0) {
	ret = False;
  }
  return ret;
}
#endif

/*
 * Yankie () provides a reference to the last saved buffer to be read
 * by other mined invocations.
 */
static
void
yankie ()
{
#ifdef linkyank
  delete_file (yankie_file);
  if (link (yank_file, yankie_file) < 0) {
	/* no error handling here as a message would inappropriately 
	   obscure the original paste buffer copy information message 
	   or an error message to the paste buffer copy function */
  }
#else
  if (copyfile (yank_file, yankie_file) == False) {
	/* no error handling here as a message would inappropriately 
	   obscure the original paste buffer copy information message 
	   or an error message to the paste buffer copy function */
  }
#endif
}

/*
 * yank_block is an interface to the actual yank.
 * It calls checkmark () to check if the marked position is still valid.
 * If it is, yank_text is called.
 */
static
void
yank_block (remove, append)
  FLAG remove;	/* == DELETE if text should be deleted */
  FLAG append;	/* == True if text should only be appended to yank buffer */
{
  switch (checkmark (mark_line, mark_text)) {
	case NOT_VALID :
		if (remove == DELETE) {
#ifdef msdos
			status_uni ("Mark not set for Cut and Paste - type Ctrl-Del to delete char, F1 k for help");
#else
			if (mined_keypad) {
				status_uni ("Mark not set for Cut and Paste - type Alt-Del to delete char, F1 k for help");
			} else {
				status_uni ("Mark not set for Cut and Paste - type F1 k for help");
			}
#endif
		} else {
			status_uni ("Mark not set for Copy and Paste - type F1 k for help");
		}
		return;
	case SMALLER :
		set_buffer_open (append);
		yank_text (yankfile (WRITE, append), & yank_status, 
				mark_line, mark_text, cur_line, cur_text, 
				remove, append);
		yankie ();
		break;
	case BIGGER :
		set_buffer_open (append);
		yank_text (yankfile (WRITE, append), & yank_status, 
				cur_line, cur_text, mark_line, mark_text, 
				remove, append);
		yankie ();
		break;
	case SAME :
		status_uni ("No text selected for Copy or Cut");
		break;
	default :
		error ("Internal mark error");
		return;
  }
}

void
yank_HTML (remove)
  FLAG remove;	/* == DELETE if text should be deleted */
{
  switch (checkmark (mark_line, mark_text)) {
	case NOT_VALID :
		error ("HTML tag selection failed");
		return;
	case SMALLER :
		yank_text (htmlfile (WRITE, False), & html_status, 
				mark_line, mark_text, cur_line, cur_text, 
				remove, False);
		break;
	case BIGGER :
		yank_text (htmlfile (WRITE, False), & html_status, 
				cur_line, cur_text, mark_line, mark_text, 
				remove, False);
		break;
	case SAME :
		error ("HTML tag selection failed");
		break;
	default :
		error ("Internal mark error");
		return;
  }
}

/*
 * COPY () puts the text between the marked position and the current
 * in the buffer.
 */
void
COPY ()
{
  if (append_flag) {
	yank_block (NO_DELETE, True);
  } else if (hop_flag > 0) {
	yank_block (NO_DELETE, True);
  } else {
	yank_block (NO_DELETE, False);
  }
}

/*
 * CUT () is essentially the same as COPY (), but the text is deleted.
 */
void
CUT ()
{
  if (viewonly) {
	viewonlyerr ();
	return;
  }

  if (append_flag) {
	yank_block (DELETE, True);
  } else if (hop_flag > 0) {
	yank_block (DELETE, True);
  } else {
	yank_block (DELETE, False);
  }
}


/*======================================================================*\
|*			Yank file handling				*|
\*======================================================================*/

/*
 * scratchfile/yankfile () tries to create a unique file in a temporary directory.
 * It tries several different filenames until one can be created
 * or MAXTRIALS attempts have been made.
 * After MAXTRIALS times, an error message is given and ERRORS is returned.
 */

#define MAXTRIALS 99

static
void
set_yank_file_name (buf_name, which, no)
  char * buf_name;
  char * which;
  int no;
{
#ifdef msdos
  build_string (buf_name, "%s-%s.%d", yankie_file, which, no);
#else
  build_string (buf_name, "%s.%s.%d_%d", yankie_file, which, getpid (), no);
#endif
}

/*
 * Delete yank file if there is one.
 */
void
delete_yank_files ()
{
/*  if (yank_status == VALID) {
	delete_file (yank_file);
  }
*/
  while (max_yank_buf_no > 0) {
	set_yank_file_name (yank_file, "buf", max_yank_buf_no);
	delete_file (yank_file);
	max_yank_buf_no --;
  }

  if (html_status == VALID) {
	delete_file (html_file);
  }
}

int
scratchfile (mode, append, buf_name, which, buf_status)
  FLAG mode;	/* Can be READ or WRITE permission */
  FLAG append;	/* == True if text should only be appended to yank buffer */
  char * buf_name;
  char * which;
  FLAG * buf_status;
{
  int fd = 0;			/* Filedescriptor to buffer */

  set_yank_file_name (buf_name, which, yank_buf_no);

/* If * buf_status == NOT_VALID, scratchfile is called for the first time */
  if (* buf_status == NOT_VALID && mode == WRITE) { /* Create new file */
	/* Generate file name. */
	/*set_yank_file_name (buf_name, which, yank_buf_no);*/
	/* Check file existence */
	if (access (buf_name, 0 /* F_OK */) == 0
	    || (
/*		fd = creat (buf_name, bufprot)		*/
		fd = open (buf_name, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, bufprot)
		) < 0)
	{
		if (++ yank_buf_no >= MAXTRIALS) {
		    if (fd == 0) {
			error2 ("Cannot create buffer file: " /* , buf_name */, "File exists");
		    } else {
			error2 ("Cannot create buffer file: " /* , buf_name */, serror ());
		    }
		    return ERRORS;
		} else {	/* try again */
		    return scratchfile (mode, append, buf_name, which, buf_status);
		}
	}
  }
  else if (* buf_status == NOT_VALID && mode == READ) {
	errno = 0;
	return ERRORS;
  }
  else /* * buf_status == VALID */
	if (  (mode == READ && (fd = open (buf_name, O_RDONLY | O_BINARY, 0)) < 0)
	   || (mode == WRITE &&
		(fd = open (buf_name, O_WRONLY | O_CREAT
				| (append ? O_APPEND : O_TRUNC)
				| O_BINARY
				, bufprot)) < 0))
  {
	* buf_status = NOT_VALID;
	return ERRORS;
  }

  clear_filebuf ();
  return fd;
}


/*======================================================================*\
|*				End					*|
\*======================================================================*/
