/*======================================================================*\
|*		Editor mined						*|
|*		file loading / text buffer functions			*|
|*		included from mined1.c					*|
\*======================================================================*/

#define dont_debug_timing

#ifdef debug_timing

#include <sys/time.h>

static
long
gettime ()
{
  struct timeval now;
  gettimeofday (& now, 0);
  return ((long) now.tv_sec) * 1000000 + now.tv_usec;
}

#define mark_time(timer)	timer -= gettime ()
#define elapsed_time(timer)	timer += gettime ()
#define elapsed_mark_time(timer1, timer2)	{long t = gettime (); timer1 += t; timer2 -= t;}

#else

#define mark_time(timer)
#define elapsed_time(timer)
#define elapsed_mark_time(timer1, timer2)

#endif


/*======================================================================*\
|*			Text buffer routines				*|
\*======================================================================*/

#define dont_debug_move

int old_x = 0;	/* previous x position */

/*
 * Find_x () returns the x coordinate belonging to address.
 * (Tabs are expanded).
 */
int
find_x (line, address)
  LINE * line;
  char * address;
{
  char * textp = line->text;
  register int x_left = line->shift_count * - SHIFT_SIZE;
  int x_in_line = 0;	/* must start from 0 to calculate correct 
			tab positions (since SHIFT_SIZE is not guaranteed 
			to be a multiple of tabsize (usually 8)) */
	/* Alright, SHIFT_SIZE is now guaranteed to be a multiple of 8 
	   due to lots of display problems related to that matter.
	   Leave this code anyway. */
  unsigned long unichar;
  int x_before = 0;
  int prevx;

  while (textp < address && * textp != '\0') {
	/* use "<" rather than "!=" because address may point amidst 
	   a UTF-8 character sequence after mode switching */
	prevx = x_in_line;
	advance_char_scr (& textp, & x_in_line, line->text);
	/* determine screen pos. to stay on if moving on a combining char. */
	if (x_in_line > prevx) {
		x_before = prevx;
	}
  }

  /* if on combining character, skip to base character */
  if (combining_mode && encoding_has_combining ()) {
	unichar = unicodevalue (textp);
	if (iscombined (unichar, textp, line->text)) {
		x_in_line = x_before;
	}
  }

#ifdef debug_move
  printf ("find_x %08X -> %d\n", address, x_in_line + x_left);
#endif
  return x_in_line + x_left;
}

/*
 * Find_address () returns the pointer in the line with given offset.
 * (Tabs are expanded).
 * find_address is only called by move_it ()
tab (cnt)		is	(((cnt) + tabsize) & ~07)
is_tab (c)		is	((c) == '\t')
 */
static
char *
find_address (line, new_x, cur_x)
  LINE * line;
  int new_x;
  int * cur_x;
{
  char * textp = line->text;
  int tx = line->shift_count * - SHIFT_SIZE;
  char * prev_poi;
  int prev_x;
  unsigned long unichar;

  while (tx < new_x && * textp != '\n') {
	if (is_tab (* textp)) {
		if (new_x == old_x /* (* cur_x) */ - 1 && tab (tx) > new_x) {
			/* Moving left over tab */
			break;
		} else if (tab_left && new_x == old_x && tab (tx) > new_x) {
			/* Moving up/down on tab */
			break;
		} else {
			tx = tab (tx);
		}
		textp ++;
	} else {
		prev_poi = textp;
		prev_x = tx;

		advance_char_scr (& textp, & tx, line->text);

		/* skip over combining characters */
		if (combining_mode && encoding_has_combining ()) {
			unichar = unicodevalue (textp);
			while (iscombined (unichar, textp, line->text)) {
				advance_char_scr (& textp, & tx, line->text);
				unichar = unicodevalue (textp);
			}
		}

		if (tx > new_x && new_x < * cur_x) {
		/* moving left into multi-width character, stay before it */
			textp = prev_poi;
			tx = prev_x;
			new_x = tx;
		}
	}
  }
#ifdef debug_move
  printf ("find_address %d (%d) -> %08X\n", new_x, * cur_x, textp);
#endif
  * cur_x = tx;
  return textp;
}

/*
 * move_to: move to given coordinates on screen.
 * move_y: move to given line on screen, staying in last explicit column.
 * move_address: move to given line at given text position.
 * The caller must check that scrolling is not needed.
 * If new x-position is < 0 or > XBREAK, move_it () will check if
 * the line can be shifted. If it can it sets (or resets) the shift_count
 * field of the current line accordingly. By this mechanism, the
 * pseudo-x-positions LINE_START / LINE_END (a very small / big value)
 * perform the appropriate positioning actions.
 * Move also sets cur_text to the right char.
 * "If we're moving to the same x coordinate, try to move to the x-coordinate
 * used on the other previous call." -- This worked erroneously and was 
 * replaced by an explicit old_x variable and move_y call.
 * move_address is directly called by move_next/previous_word(), 
 * re_search(), RDwin(), load_file(), justi(), JUSandreturn()
 */
static
void
move_it (new_x, new_address, new_y)
  register int new_x;
  int new_y;
  char * new_address;
{
  register LINE * line = cur_line;	/* For building new cur_line */
  register int lineno = line_number;
  int shift = 0;			/* How many shifts to make */
  char * utf_search;
  char * utf_prev;
  char * char_begin;

/*  static int rel_x = 0;	*/	/* Remember relative x position */
/*	This was used as a trick to stay virtually in the previous column 
	even when moving across shorter lines; but it had >= 2 errors.
	Renamed to old_x, made globally accessible and explicitly used 
	by appropriate calls to avoid these problems. TW */
  int tx = x;

/* Check for illegal values */
  if (new_y < 0 || new_y > last_y) {
	return;
  }

/* Adjust y-coordinate and cur_line */
  if (new_y < y) {
	while (y != new_y) {
		y --;
		line = line->prev;
		lineno --;
	}
  } else {
	while (y != new_y) {
		y ++;
		line = line->next;
		lineno ++;
	}
  }

/* Set or unset relative x-coordinate */
  if (new_address == NIL_PTR) {
	new_address = find_address (line, new_x, & tx);
	new_x = tx;
  } else {
	/* rel_x = */ new_x = find_x (line, new_address);
  }

/* Adjust on character boundary */
  if (cjk_text) {
	char_begin = charbegin (line->text, new_address);
	if (char_begin != new_address) {
		/* adjust position which is currently within a 
		   CJK double-width, multi-byte character */
		if (new_x >= x) {
			if (* new_address != '\n') {
				new_x ++;
				new_address = char_begin;
				advance_char (& new_address);
			} else {
				/* incomplete CJK half character */
			}
		} else {
			new_x --;
			new_address = char_begin;
		}
	}
  } else if (utf8_text) {
	if ((* new_address & 0xC0) == 0x80) {	/* UTF-8 sequence byte? */
	/* adjust position which is currently within a UTF-8 character */
	/* adjust new_x for multi-width characters ? */
		utf_search = line->text;
		utf_prev = utf_search;
		while (utf_search < new_address) {
			utf_prev = utf_search;
			advance_utf8 (& utf_search);
		}
		if (utf_search != new_address) {
			if (new_x >= x) {
				new_address = utf_search;
			} else {
				new_address = utf_prev;
			}
		}
	}
  }

/* Adjust shift_count if new_x lower than 0 or higher than XBREAK */
/* Allow adjustment also if new_x == 0 to enable left shift mark */
  if (new_x <= 0
      || new_x >= XBREAK
      || (new_x == XBREAK - 1 && iswide (unicodevalue (new_address)))
     ) {
	if (new_x > XBREAK
	    || (new_x == XBREAK && * new_address != '\n')
	    || new_x == XBREAK - 1
	   ) {
		shift = (new_x - XBREAK) / SHIFT_SIZE + 1;
	} else {
		shift = new_x / SHIFT_SIZE;
		if (new_x % SHIFT_SIZE) {
			shift --;
		}
		if (new_x == 0 && line->shift_count != 0 && marker_defined (SHIFT_BEG_marker, UTF_SHIFT_BEG_marker)) {
			/* do not stay on left shift marker; adjust line */
			shift --;
		}
	}

	if (shift != 0) {
		line->shift_count += shift;
		new_x = find_x (line, new_address);
		if (new_x == 0 && line->shift_count != 0 && marker_defined (SHIFT_BEG_marker, UTF_SHIFT_BEG_marker)) {
			/* do not stay on left shift marker; adjust line */
			line->shift_count --;
			new_x = find_x (line, new_address);
		}
		set_cursor (0, y);
		line_print (y, line);
		/* rel_x = new_x; */
	}
  }

/* Assign and position cursor */
  x = new_x;
  cur_text = new_address;
  cur_line = line;
  calc_line_no ();
  line_number = lineno;
  set_cursor_xy ();
}

void
move_y (ny)
  register int ny;
{
  move_it (old_x, NIL_PTR, ny);
}

void
move_to (nx, ny)
  register int nx;
  register int ny;
{
  old_x = x;
  move_it (nx, NIL_PTR, ny);
  old_x = x;
}

void
move_address (nadd, ny)
  register char * nadd;
  register int ny;
{
  old_x = x;
  move_it (0, nadd, ny);
  old_x = x;
}

void
move_address_w_o_RD (nadd, ny)
  register char * nadd;
  register int ny;
{
  old_x = x;
  move_it (0, nadd, ny);
  old_x = x;
}


/*
 * Just count to determine the current line number.
 */
void
calc_line_no ()
{
  register LINE * line;
  register int line_num = 0;

	for (line = header->next; line != cur_line; line = line->next) {
		line_num ++;
	}
	line_number = line_num + 1;
}

/*
 * Proceed returns the count'th line after 'line'. When count is negative
 * it returns the count'th line before 'line'. When the next (previous)
 * line is the tail (header) indicating EOF (tof) it stops.
 */
LINE *
proceed (line, count)
  register LINE * line;
  register int count;
{
  if (count < 0) {
	while (count ++ < 0 && line != header) {
		line = line->prev;
	}
  } else {
	while (count -- > 0 && line != tail) {
		line = line->next;
	}
  }
  return line;
}

/*
 * Reset assigns bot_line, top_line and cur_line according to 'head_line'
 * which must be the first line of the screen, and a y-coordinate,
 * which will be the current y-coordinate (if it isn't larger than last_y)
 */
void
reset (head_line, screen_y)
  LINE * head_line;
  int screen_y;
{
  register LINE * line;

  top_line = line = head_line;

/* Search for bot_line (might be last line in file) */
  for (last_y = 0; 
	last_y < total_lines - 1 && last_y < SCREENMAX && line->next != tail; 
	last_y ++
      ) {
	line = line->next;
  }

  bot_line = line;
  y = (screen_y > last_y) ? last_y : screen_y;

/* Set cur_line according to the new y value */
  cur_line = proceed (top_line, y);
  calc_line_no ();
}


/*======================================================================*\
|*			Character statistics				*|
\*======================================================================*/

/*
 * utf8_count () returns the number of UTF-8 characters in the string
   (like char_count) and also detects quotation marks and updates 
   their statistics.
 */
static
int
utf8_count (string)
  char * string;
{
  char * start = string;
  int count = 0;
  unsigned long unichar;
  int utflen;

  if (string != NIL_PTR) {
    while (* string != '\0') {
	/* Detect quotation marks.
	   The UTF-8 codes of all quotation marks are either 
	   C2AB or C2BB or start with either E280 or E380. 
	   This may help for efficient detection during file loading.
	 */
	if ((((character) * string) <= 0x27
	     && (* string == '\'' || * string == '"')
	    )
	    ||
	    ((* string & 0xDE) == 0xC2
	     && (((character) * string) == 0xC2 || ((character) * (string + 1)) == 0x80)
	    )
	   )
	{
	    utf8_info (string, & utflen, & unichar);
	    switch ((unsigned int) unichar) {
	    case (character) '"':
	    case (character) '\'':
			count_plain ++;
			break;
	    case 0x201C: /* “ LEFT DOUBLE QUOTATION MARK; DOUBLE TURNED COMMA QUOTATION MARK */
	    case 0x2018: /* ‘ LEFT SINGLE QUOTATION MARK; SINGLE TURNED COMMA QUOTATION MARK */
			/* left English, Spanish, Turkish */
			/* right German, Danish, Polish, Russian, Romanian, Slovak, Slovenian, Czech, Hungarian */
		if (isopeningquote (string, start)) {
			count_English ++;
		} else {
			count_German ++;
		}
		break;
	    case 0x201D: /* ” RIGHT DOUBLE QUOTATION MARK; DOUBLE COMMA QUOTATION MARK */
		count_Greek ++;
	    case 0x2019: /* ’ RIGHT SINGLE QUOTATION MARK; SINGLE COMMA QUOTATION MARK */
			/* right English, Spanish, Turkish */
			/* right Dutch, Hungarian */
			/* left/right Swedish, Finnish */
			/* ” right nested traditional Greek */
		count_Swedish_q ++;
		if (! isopeningquote (string, start)) {
			count_English ++;
			count_Dutch ++;
		}
		break;
	    case 0x201E: /* „ DOUBLE LOW-9 QUOTATION MARK; LOW DOUBLE COMMA QUOTATION MARK */
	    case 0x201A: /* ‚ SINGLE LOW-9 QUOTATION MARK; LOW SINGLE COMMA QUOTATION MARK */
			/* left German, Danish, Polish, Russian, Romanian, Slovak, Sloven, Czech, Hungarian */
			/* left Dutch, Hungarian */
		if (isopeningquote (string, start)) {
			count_German ++;
			count_Dutch ++;
		}
		break;
	    case 0x201F: /* ‟ DOUBLE HIGH-REVERSED-9 QUOTATION MARK; DOUBLE REVERSED COMMA QUOTATION MARK */
		count_Greek ++;
	    case 0x201B: /* ‛ SINGLE HIGH-REVERSED-9 QUOTATION MARK; SINGLE REVERSED COMMA QUOTATION MARK */
			/* ‟ left nested traditional Greek */
		break;
	    case 0x00AB: /* « LEFT-POINTING DOUBLE ANGLE QUOTATION MARK; LEFT POINTING GUILLEMET */
	    case 0x2039: /* ‹ SINGLE LEFT-POINTING ANGLE QUOTATION MARK; LEFT POINTING SINGLE GUILLEMET */
			/* left French, Italian, Norwegian, Portuguese, Russian, Slovenian, Turkish */
			/* right German, Polish, Slovak, Czech, Serbian, Croatian */
			/* left Greek */
		if (isopeningquote (string, start)) {
			count_French ++;
			count_Greek ++;
		} else {
			count_German ++;
			count_inwards ++;
		}
		break;
	    case 0x00BB: /* » RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK; RIGHT POINTING GUILLEMET */
	    case 0x203A: /* › SINGLE RIGHT-POINTING ANGLE QUOTATION MARK; RIGHT POINTING SINGLE GUILLEMET */
			/* right French, Italian, Norwegian, Portuguese, Russian, Slovenian, Turkish */
			/* left German, Polish, Slovak, Czech, Serbian, Croatian */
			/* left/right Swedish, Finnish */
			/* right Greek */
		count_Swedish_g ++;
		if (isopeningquote (string, start)) {
			count_German ++;
			count_inwards ++;
		} else {
			count_French ++;
			count_Greek ++;
		}
		break;
	    case 0x300A: /* 《 LEFT DOUBLE ANGLE BRACKET; OPENING DOUBLE ANGLE BRACKET */
	    case 0x3008: /* 〈 LEFT ANGLE BRACKET; OPENING ANGLE BRACKET */
			/* left Chinese? */
			/* 〈 left Chinese?? */
		if (isopeningquote (string, start)) {
			count_Chinese ++;
		}
		break;
	    case 0x300B: /* 》 RIGHT DOUBLE ANGLE BRACKET; CLOSING DOUBLE ANGLE BRACKET */
	    case 0x3009: /* 〉 RIGHT ANGLE BRACKET; CLOSING ANGLE BRACKET */
			/* right Chinese? */
			/* 〉 right Chinese?? */
		if (! isopeningquote (string, start)) {
			count_Chinese ++;
		}
		break;
	    case 0x300C: /* 「 LEFT CORNER BRACKET; OPENING CORNER BRACKET */
	    case 0x300E: /* 『 LEFT WHITE CORNER BRACKET; OPENING WHITE CORNER BRACKET */
			/* left Japanese */
			/* 『 left Japanese? */
		if (isopeningquote (string, start)) {
			count_Japanese ++;
		}
		break;
	    case 0x300D: /* 」 RIGHT CORNER BRACKET; CLOSING CORNER BRACKET */
	    case 0x300F: /* 』 RIGHT WHITE CORNER BRACKET; CLOSING WHITE CORNER BRACKET */
			/* right Japanese */
			/* 』 right Japanese? */
		if (! isopeningquote (string, start)) {
			count_Japanese ++;
		}
		break;
	    }
	}

	/* Advance and count */
	advance_utf8 (& string);
	count ++;
    }
  }
  return count;
}


/*======================================================================*\
|*			File operations					*|
\*======================================================================*/

/*
 * Initialize is called when a another file is edited. It free's the allocated 
 * space and sets modified back to False and fixes the header/tail pointer.
 */
static
void
initialize ()
{
  register LINE * line;
  register LINE * next_line;

/* Delete the whole list */
  for (line = header->next; line != tail; line = next_line) {
	next_line = line->next;
	free_space (line->text);
	free_header (line);
  }

/* header and tail should point to itself */
  line->next = line->prev = line;
  x = y = 0;
  rpipe = modified = False;
}

/*
 * Get_line reads one line from filedescriptor fd. If EOF is reached on fd,
 * get_line () returns ERRORS, else it returns the length of the string.
 */
static char * get_l_err_u1;
static char * get_l_err1;
static character last_byte = '\0';

static long count_good_utf;	/* count good UTF-8 sequences */
static long count_bad_utf;	/* count bad UTF-8 sequences */
static long count_utf_bytes;	/* count UTF-8 sequence bytes */
static long count_good_iso;	/* count good ISO-8859 bytes */
static long count_good_viscii;	/* count good VISCII bytes */
static long count_read_op;	/* count 1st read operation by get_line */
static long count_lineend_LF;	/* count Unix lines */
static long count_lineend_CRLF; /* count MSDOS lines */
static FLAG UTF8_BOM;		/* UTF-8 BOM found at beginning of file? */
static FLAG Unicode_BOM;	/* Unicode BOM found at beginning of file? */
static FLAG Unicode_little_endian;	/* Unicode words reversed? */

/*
   CJK character encoding auto-detection
 */
static character last_cjkbyte = '\0';
static long count_good_cjk;	/* count good CJK encodings */
static long count_weak_cjk;	/* count weak (unsure) CJK encodings */
static long count_bad_cjk;	/* count bad CJK encodings */
static long count_big5;		/* count Big5 encodings */
static long count_gb;		/* count GB (GB2312, GBK, GB18030) encodings */
static int detect_gb18030 = 0;	/* observe GB18030 byte state */
static long count_uhc;		/* count UHC (KS C 5601/KS X 1001) encodings */
static long count_jp;		/* count JIS (EUC-JP) encodings */
static long count_sjis;		/* count Shift-JIS encodings */
static long count_johab;	/* count Johab encodings */
static long count_cns;		/* count CNS encodings */


void
reset_get_line ()
{
  get_l_err1 = NIL_PTR;
  get_l_err_u1 = NIL_PTR;

  count_good_utf = 0;
  count_bad_utf = 0;
  count_utf_bytes = 0;
  count_good_iso = 0;
  count_good_viscii = 0;
  count_good_cjk = 0;
  count_weak_cjk = 0;
  count_bad_cjk = 0;
  count_big5 = 0;
  count_gb = 0;
  count_uhc = 0;
  count_jp = 0;
  count_sjis = 0;
  count_johab = 0;
  count_cns = 0;
  count_read_op = 0;
  count_lineend_LF = 0;
  count_lineend_CRLF = 0;
  reset_quote_statistics ();

  last_byte = '\0';
  last_cjkbyte = '\0';
  UTF8_BOM = False;
  Unicode_BOM = False;
  utf16_text = False;
}

static
void
show_get_l_error (get_l_err)
  char * get_l_err;
{
  if (get_l_err != NIL_PTR) {
	ring_bell ();
	error2 (get_l_err, " -- type a blank");
	while (readcharacter () != ' ' && quit == False) {
		ring_bell ();
		flush ();
	}
  }
}

void
show_get_l_errors ()
{
  show_get_l_error (get_l_err1);
  show_get_l_error (get_l_err_u1);
  clear_status ();
}

static unsigned int surrogate = 0;

/*
   Transform UTF-16 input into UTF-8.
 */
static
int
UTF8_transform (UTF8buf, maxbufl, next_byte_poi, fini_byte)
  char * UTF8buf;
  int maxbufl;
  character * * next_byte_poi;
  character * fini_byte;
{
  register char * ptr = UTF8buf;
  int read_chars = 0;
  unsigned long unichar;

  while (read_chars + 4 < maxbufl && * next_byte_poi < fini_byte) {
	unichar = * * next_byte_poi;
	(* next_byte_poi) ++;
	if (* next_byte_poi < fini_byte) {
		if (Unicode_little_endian) {
			unichar |= (* * next_byte_poi) << 8;
		} else {
			unichar = (unichar << 8) | (* * next_byte_poi);
		}
		(* next_byte_poi) ++;
	} else if (Unicode_little_endian == False) {
		unichar = 0;
	}

	if ((unichar & 0xFC00) == 0xD800) {
	/* high surrogates */
		surrogate = (unichar - 0xD7C0) << 10;
	} else if ((unichar & 0xFC00) == 0xDC00) {
	/* low surrogates */
		unichar = surrogate | (unichar & 0x03FF);
		surrogate = 0;
		* ptr ++ = 0xF0 | (unichar >> 18);
		* ptr ++ = 0x80 | ((unichar >> 12) & 0x3F);
		* ptr ++ = 0x80 | ((unichar >> 6) & 0x3F);
		* ptr ++ = 0x80 | (unichar & 0x3F);
		read_chars += 4;
	} else if (unichar < 0x80) {
		* ptr ++ = unichar;
		read_chars ++;
	} else if (unichar < 0x800) {
		* ptr ++ = 0xC0 | (unichar >> 6);
		* ptr ++ = 0x80 | (unichar & 0x3F);
		read_chars += 2;
	} else {
		* ptr ++ = 0xE0 | (unichar >> 12);
		* ptr ++ = 0x80 | ((unichar >> 6) & 0x3F);
		* ptr ++ = 0x80 | (unichar & 0x3F);
		read_chars += 3;
	}
  }

  return read_chars;
}

#define dont_debug_read
#ifdef debug_read
#define trace_read(params)	printf params
#else
#define trace_read(params)	
#endif

static char * last = NIL_PTR;
static char * current = NIL_PTR;
static char * UTF16buf = NIL_PTR;
static char * next_byte;
static char * fini_byte;
static long read_bytes;
static long read_chars;

static
void
clear_get_line ()
{
  last = NIL_PTR;
  current = NIL_PTR;
}

int
get_line (fd, buffer, len)
  int fd;
  register char buffer [MAX_CHARS];
  int * len;
{
  register char * cur_pos = current;
  char * begin = buffer;
  char * fini = (char *) (buffer + MAX_CHARS - 2) /* leave space for '\n\0' */;
  register FLAG ignore1char;
  int ret = 0;
  char char_copied;
  int Ulineend_state = 0;
  character curbyte;

#define dont_debug_overlong
#ifdef debug_overlong
  fini = (char *) (buffer + 20) /* debug overlong line input */;
#endif

  /* read one line */
  do {	/* read one char */
	do {	/* read one effective (not to be filtered out) char */
	    ignore1char = False;
	    if (cur_pos == last) {
		if (Unicode_BOM) {
		    if (next_byte >= fini_byte) {
			do {
				interrupted = False;
				read_bytes = read (fd, UTF16buf, screen_BUFL);
				trace_read (("read %d\n", read_bytes));
			} while (interrupted ||
				 (read_bytes == -1 && geterrno () == EINTR));
			if (read_bytes <= 0) {
				read_chars = read_bytes;
				break;
			}
			next_byte = UTF16buf;
			fini_byte = & UTF16buf [read_bytes];
		    }
		    read_chars = UTF8_transform (screen, screen_BUFL, & next_byte, fini_byte);
		} else {
		    do {
			interrupted = False;
			read_chars = read (fd, screen, screen_BUFL);
			trace_read (("read %d\n", read_chars));
		    } while (interrupted ||
			     (read_chars == -1 && geterrno () == EINTR));
		    if (read_chars <= 0) {
			break;
		    }
		}
		last = & screen [read_chars];
		cur_pos = screen;

		if (count_read_op == 0) {
			if (strncmp (screen, "﻿", 3) == 0) {
				/* UTF-8 BOM */
				UTF8_BOM = True;
			} else if (strncmp (screen, "\376\377", 2) == 0
				   && auto_detect_utf) {
				/* UTF-16 BOM */
				Unicode_BOM = True;
				Unicode_little_endian = False;
				utf16_text = True;
				utf16_little_endian = False;
				UTF8_BOM = True;
				/* remove BOM from text: skip UTF-8 BOM */
				cur_pos += 3;
			} else if (strncmp (screen, "\377\376", 2) == 0
				   && auto_detect_utf) {
				/* little-endian reversed UTF-16 BOM */
				Unicode_BOM = True;
				Unicode_little_endian = True;
				utf16_text = True;
				utf16_little_endian = True;
				UTF8_BOM = True;
				/* remove BOM from text: skip UTF-8 BOM */
				cur_pos += 3;
			}
			if (Unicode_BOM) {
			/*	get_l_err_u0 = "Converted UTF-16 input"; */

				/* move UTF-16 input to UTF-16 buffer */
				if (UTF16buf == NIL_PTR) {
					UTF16buf = alloc (screen_BUFL);
					if (UTF16buf == NIL_PTR) {
						get_l_err_u1 = "Cannot allocate memory for UTF-16 buffer";
						viewonly = True;
						modified = False;
						return ERRORS;
					}
				}
				memcpy (UTF16buf, screen, (unsigned int) read_chars);
				read_bytes = read_chars;
				next_byte = UTF16buf;
				fini_byte = & UTF16buf [read_bytes];

				/* transform to UTF-8 */
				read_chars = UTF8_transform (screen, screen_BUFL, & next_byte, fini_byte);
				last = & screen [read_chars];
			}
			count_read_op = 1;
		}
	    }

	    curbyte = * cur_pos;


	    /* begin character encoding auto-detection */

	    /* UTF-8 auto-detection */
	    if (count_utf_bytes == 0) {
		if ((curbyte & 0xC0) == 0x80) {
			count_bad_utf ++;
		} else {
			count_utf_bytes = UTF8_len (curbyte) - 1;
		}
	    } else if ((curbyte & 0xC0) == 0x80) {
		count_utf_bytes --;
		if (count_utf_bytes == 0) {
			count_good_utf ++;
		}
	    } else {
		count_bad_utf ++;
		count_utf_bytes = 0;
	    }

	    /* VISCII and ISO-8859 auto-detection */
	    if (curbyte >= 0x80) {
		count_good_viscii ++;

		/* Defending ISO-8859 vs. CJK auto-detection */
		if (curbyte >= 0xA0) {
			count_good_iso ++;
		} else {
			count_good_iso --;
		}

	    } else if (auto_detect_utf) {
		switch (curbyte) {
			case '':
			case '':
			case '':
			case '':
			case '':
			case '':
				count_good_viscii += 9;
				break;
			case '\000':
			case '':
			case '':
			case '':
			case '':
			case '':
			case '':
			case '':
			case '':
			case '':
			case '':
			case '':
			case '':
			case '':
			case '':
			case '':
			case '':
			case '':
			case 0x1A:
			case '':
			case '':
			case '':
			case '':
				count_good_viscii -= 3;
				break;
		}
	    }


	    /* CJK/Han encoding auto-detection */
	    /* maintain GB18030 detection state */
	    if (detect_gb18030 != 0) {
		detect_gb18030 --;
	    }

	    /* perform detection on bytes after non-ASCII bytes */
	    if (last_cjkbyte >= 0x80) {
	      if (curbyte >= 0x80) {
			count_good_cjk ++;
	      } else if (curbyte < 0x30) {
			count_bad_cjk ++;
	      } else if (curbyte <= 0x39) {
			if (detect_gb18030 == 1) {
				count_good_cjk ++;
			}
	      } else {
			count_weak_cjk ++;
	      }


	      /* detect specific CJK encoding */
	      if (auto_detect_cjk) {

		/* CJK character set ranges
			Big5	Big5-HKSCS	87-FE	40-7E, A1-FE
			GB	GBK		81-FE	40-7E, 80-FE
				GB18030		81-FE	30-39	81-FE	30-39
			UHC	UHC		81-FE	41-5A, 61-7A, 81-FE
			JIS	EUC-JP		8E	A1-DF
						A1-FE	A1-FE
						8F	A1-FE	A1-FE
			JIS	Shift-JIS	A1-DF
						81-9F, E0-EF	40-7E, 80-FC
			CNS	EUC-TW		A1-FE	A1-FE
						8E	A1-A7	A1-FE	A1-FE
		*/

	       if (last_cjkbyte >= 0x81 && last_cjkbyte <= 0xFE) {

		/*	Big5	Big5-HKSCS	87-FE	40-7E, A1-FE
		*/
		if (last_cjkbyte >= 0x87 /* && last_cjkbyte <= 0xFE */
		    && ((curbyte >= 0x40 && curbyte <= 0x7E)
		        || (curbyte >= 0xA1 && curbyte <= 0xFE)))
		{
			count_big5 ++;
		}

		/*	GB	GBK		81-FE	40-7E, 80-FE
				GB18030		81-FE	30-39	81-FE	30-39
			UHC	UHC		81-FE	41-5A, 61-7A, 81-FE
		*/
		/* if (last_cjkbyte >= 0x81 && last_cjkbyte <= 0xFE) { */
		    if ((curbyte >= 0x40 && curbyte <= 0x7E)
		        || (curbyte >= 0x80 && curbyte <= 0xFE)) {
			count_gb ++;

			if ((curbyte >= 0x41 && curbyte <= 0x5A)
			        || (curbyte >= 0x61 && curbyte <= 0x7A)
			        || (curbyte >= 0x81 /* && curbyte <= 0xFE */))
			{
				count_uhc ++;
			}

		    } else if (curbyte >= 0x30 && curbyte <= 0x39) {
			if (detect_gb18030 == 1) {
				count_gb += 5;
			} else {
				detect_gb18030 = 3;
			}
		    }
		/* } */

		/*	JIS	EUC-JP		A1-FE	A1-FE
						8F	A1-FE	A1-FE
						8E	A1-DF
		*/
		if (((last_cjkbyte >= 0xA1 && last_cjkbyte <= 0xFE)
		     || last_cjkbyte == 0x8E
		    )
		    && curbyte >= 0xA1 && curbyte <= 0xFE)
		{
			count_jp ++;
		}

		/*	JIS	Shift-JIS	A1-DF
						81-9F, E0-EF	40-7E, 80-FC
		*/
		if (last_cjkbyte >= 0xA1 && last_cjkbyte <= 0xDF) {
		    if (curbyte >= 0xA1 && curbyte <= 0xDF) {
			/* two consecutive single-byte SJIS characters */
			count_sjis += 2;
		    }
		} else {
		    if (curbyte >= 0x40 && curbyte <= 0xFC && curbyte != 0x7F) {
			count_sjis ++;
		    }
		}

		/*	Johab	Johab		84-DE, E0-F9	31-7E, 81-FE
		*/
		if (((last_cjkbyte >= 0x84 && last_cjkbyte <= 0xDE)
		        || (last_cjkbyte >= 0xE0 && last_cjkbyte <= 0xF9))
		    && ((curbyte >= 0x31 && curbyte <= 0x7E)
		        || (curbyte >= 0x81 && curbyte <= 0xFE)))
		{
			count_johab ++;
		}

	       } /* if (last_cjkbyte >= 0x81 && last_cjkbyte <= 0xFE) */
	         else { 
		    if (curbyte >= 0x40 && curbyte <= 0xFC && curbyte != 0x7F) {
			count_sjis ++;
		    }
	       }
	      } /* if auto_detect_cjk */

	      /* reset CJK byte state */
	      last_cjkbyte = '\0';
	    } else {
	      last_cjkbyte = curbyte;
	    }

	    /* end character encoding auto-detection */


#ifdef msdos_with_auto_crlf
	    if (curbyte == '\n' && last_byte != '\r') {
		set_modified ();
	    }
#endif

	    /* line-end transformation */
	    if (RET_opt == 'R' && curbyte == '\n' && last_byte == '\r') {
		/* handling DOS line-ends in Mac mode */
		last_byte = curbyte;
		cur_pos ++;
		ignore1char = True;
		set_modified ();
	    } else {
		last_byte = curbyte;
	    }

	    if (* cur_pos == '\r' ) {
		if (RET_opt == 'R') {
		/* convert Mac line-ends */
			* cur_pos = '\n';
			set_modified ();
		} else if (RET_opt == 'r') {
		/* convert DOS line-ends */
			cur_pos ++;
			if (* cur_pos == '\n') {
				ignore1char = True;
#ifndef pc
				set_modified ();
#endif
			} else {
				cur_pos --;	/* leave '^M' within line */
			}
		}
	    }

	    /* NUL character handling */
	    if (* cur_pos == '\0') {
		* cur_pos = '\n';
		ret = NUL_LINE;
	    }

	} while (ignore1char);

	/* detect if no more lines available */
	if (cur_pos == last) {
		break;
	}

	/* handle if line buffer full */
	* buffer = * cur_pos;
	if (buffer > fini - 6 && * cur_pos != '\n') {
	    /* try not to split within a UTF-8 sequence */
	    if (buffer == fini ||	/* last chance to split! */
		(cjk_text && auto_detect_utf == False
		? charbegin (begin, buffer) == buffer
		: (* cur_pos & 0xC0) != 0x80
		))
	    {
		get_l_err1 = "Line(s) too long - split";
		* buffer ++ = '\n';

		/* indicate split line */
		if (linesplitmarker == '\0') {
			/* default: new line splitting */
			ret = SPLIT_LINE;
		} else {
			cur_pos --;
			* cur_pos = linesplitmarker;
			/* To provide safe space for this operation, the 
			   screen array (which cur_pos points into) is 
			   preceded by the char variable linesplitmarker, 
			   which also holds the marker character */
		}

		break;
	    }
	}

	/* copy char from input buffer into line buffer */
	char_copied = * buffer ++ = * cur_pos ++;
	if (utf8_lineends) {
		if (Ulineend_state == 0 && char_copied == '�') {
			Ulineend_state = 1;
		} else if (Ulineend_state > 0) {
			if (Ulineend_state == 1 && char_copied == '�') {
				Ulineend_state = 2;
			} else if (Ulineend_state == 2 && char_copied == '�') {
				Ulineend_state = 3;
				/* LS   detected */
				char_copied = '\n';
			} else if (Ulineend_state == 2 && char_copied == '�') {
				Ulineend_state = 3;
				/* PS   detected */
				char_copied = '\n';
			} else {
				Ulineend_state = 0;
			}
		}
	}
  } while (char_copied != '\n' &&
		(char_copied != '\r' || RET_opt != 'M')
	  );

  current = cur_pos;
  if (read_chars <= 0) {
	if (buffer == begin) {
		return ERRORS;
	}
	if (* (buffer - 1) != '\n' &&
		(* (buffer - 1) != '\r' || RET_opt != 'M')
	   )
	{
		if (loading) {
		/* Add '\n' to last line of file, for internal handling */
			* buffer ++ = '\n';
			* buffer = '\0';
		/* but inform load_file of missing line end */
			ret = NO_LINE;
		} else {
			* buffer = '\0';
			ret = NO_LINE;
		}
	}
  }

  * buffer = '\0';
  * len = (int) (buffer - begin);
  return ret;
}

/*
 * get_open_pos and save_open_pos look up and save the current file 
   position in @mined.mar
   For save_open_pos, line_number must be up-to-date
 */
static
void
get_open_pos (fn)
  char * fn;
{
  int mark_fd = open (mark_file, O_RDONLY | O_BINARY, 0);
  char * spoi;
  char * npoi;
  FLAG modif = modified;
  int dumlen;
  FLAG get_quote_type = quote_type == 0;

  open_linum = 0;
  open_col = 0;
  if (mark_fd >= 0) {
	while ((get_line (mark_fd, text_buffer, & dumlen)) != ERRORS) {
	    if (strncmp (fn, text_buffer, strlen (fn)) == 0) {
		spoi = text_buffer + strlen (fn);
		if (* spoi == ' ') {
			int v4, v5, v6 = -1;
			lines_per_page = 0;
			spoi = scan_int (spoi, & open_linum);
			spoi = scan_int (spoi, & open_col);
			spoi = scan_int (spoi, & lines_per_page);
			spoi = scan_int (spoi, & v4);
			if (v4 >= 0) {
				JUSlevel = 1;
				spoi = scan_int (spoi, & v5);
				spoi = scan_int (spoi, & v6);
				if (v6 > 0) {
					first_left_margin = v4;
					next_left_margin = v5;
					right_margin = v6;
				}
			} else {
				JUSlevel = 0;
			}
			if (get_quote_type) {
				int vq = -1;
				spoi = scan_int (spoi, & vq);
				if (vq >= 0) {
					/* legacy int entries */
					if (vq == 1) {
						set_quote_style ("“” ‘’");
					} else if (vq == 2) {
						set_quote_style ("„“ ‚‘");
					} else if (vq == 3) {
						set_quote_style ("«» ‹›");
					} else if (vq == 4) {
						set_quote_style ("»« ›‹");
					} else if (vq == 5) {
						set_quote_style ("„” ‚’");
					} else if (vq == 6) {
						set_quote_style ("”” ’’");
					} else if (vq == 7) {
						set_quote_style ("»» ››");
					} else if (vq == 8) {
						set_quote_style ("『』 「」");
					} else {
						set_quote_type (0);
					}
				} else {
					/* string entries */
					while (* spoi == ' ') {
						spoi ++;
					}
					npoi = strchr (spoi, '\n');
					if (npoi != NIL_PTR) {
						* npoi = '\0';
					}
					set_quote_style (spoi);
				}
			}
		}
	    }
	}
	(void) close (mark_fd);
	clear_buffer ();
  }
  /* prevent affecting the modified flag when loading the line number file */
  modified = modif;
}

int
get_cur_col ()
{
  return cur_line->shift_count * SHIFT_SIZE + x;

/*
  int cur_column = 0;
  char * cpoi = cur_line->text;
  while (* cpoi != '\0' && cpoi != cur_text) {
	advance_char_scr (& cpoi, & cur_column, cur_line->text);
  }
  return cur_column;
*/
}

/**
   Save file position and other information in mined marker file.
   Return False if saving to the file fails.
 */
static
FLAG
save_open_pos (fn, force)
  char * fn;
  int force;
{
  int mark_fd;
  char marktext [maxLINE_LEN];
  int cur_column;

  if (fn [0] != '\0') {
    if (force > 0) {
	mark_fd = open (mark_file, O_WRONLY | O_APPEND | O_CREAT, fprot);
    } else {
	mark_fd = open (mark_file, O_WRONLY | O_APPEND, fprot);
    }
    if (mark_fd >= 0) {
	FLAG ret = True;
	cur_column = get_cur_col ();
	if (JUSlevel > 0) {
	    build_string (marktext, "%s %d %d %d %d %d %d %s\n",
			  fn, line_number, cur_column, lines_per_page, 
			  first_left_margin, next_left_margin, right_margin, 
			  quote_mark (quote_type, 0));
	} else {
	    build_string (marktext, "%s %d %d %d -3 %s\n",
			  fn, line_number, cur_column, lines_per_page, 
			  quote_mark (quote_type, 0));
	}
	if (write (mark_fd, marktext, strlen (marktext)) <= 0) {
		ret = False;
	}
	if (close (mark_fd) < 0) {
		ret = False;
	}
	return ret;
    } else {
	return False;
    }
  }
  return True;
}

/*
 * extract_lineend_type extracts the type of line end from the string, 
 * removes it from the string and returns it
 */
lineend_type
extract_lineend_type (text, length)
  char * text;
  int length;
{
  register char * end = text + length - 1;

  if (* end == '\n') {
	end --;
	if (length > 1 && * end == '\r') {
		* end = * (end + 1);
		* (end + 1) = '\0';
		count_lineend_CRLF ++;	/* count MSDOS lines */
		return lineend_CRLF;
	} else {
		count_lineend_LF ++;	/* count Unix lines */
		return lineend_LF;
	}
  } else if (* end == '\r') {
	* end = '\n';
	return lineend_CR;
  } else if (utf8_lineends && length >= 3 && strncmp (end - 2, " ", 3) == 0) {
	end --;
	end --;
	* end ++ = '\n';
	* end = '\0';
	return lineend_LS;	/* Unicode line separator 2028 */
  } else if (utf8_lineends && length >= 3 && strncmp (end - 2, " ", 3) == 0) {
	end --;
	end --;
	* end ++ = '\n';
	* end = '\0';
	return lineend_PS;	/* Unicode paragraph separator 2029 */
  } else {
	return lineend_NONE;
  }
}


static
void
update_file_name (newname, update)
  char * newname;
  FLAG update;
{
  char * b = basename (newname);
  copy_string (file_name, newname);	/* Save file name */
  if (hide_password_mode == 1) {
	FLAG old_hide_password = hide_password;
	hide_password = * b == '.';
	if (update && hide_password != old_hide_password) {
		RDwin ();
	}
  }
}

#define dont_debug_auto_detect

/*
 * Load_file loads the file with given name or the input pipe into memory.
 * If the file couldn't be opened, just an empty line is installed.
 * Buffer pointers are initialized.
 * The current position is set to the last saved position.
 *
 * Load_file_position loads and positions as follows:
	if open_linum > 0, the given position is used
	if open_linum == 0, the last saved position is used
	if open_linum < 0, no position is set (stays at beginning)
 */
static
void
load_file_position (file, with_display, to_open_linum, to_open_col)
  char * file;
  FLAG with_display;
  int to_open_linum;
  int to_open_col;
{
  register LINE * line = header;
  int len;
  int ret;
  long nr_of_bytes = 0L;
  long nr_of_chars = 0L;
  long nr_of_utfchars = 0L;
  long nr_of_cjkchars = 0L;
  int fd = -1;		/* Filedescriptor for file */
  FLAG locked = False;	/* lockf performed ? */
  lineend_type new_return_type;
  FLAG save_utf8_text;	/* save state of utf8_text */
  FLAG save_cjk_text;	/* save state of cjk_text */
  FLAG detect_cjk = auto_detect_cjk;
  char prev_encoding_tag;
  FLAG empty = False;
  char * prev_text_encoding = get_text_encoding ();

  /* restore determined default text encoding */
  set_text_encoding (default_text_encoding, ' ');

  total_lines = 0;	/* Zero lines to start with */

  open_linum = to_open_linum;
  open_col = to_open_col;

  clearscreen ();
  flush ();
  overwriteOK = False;
  writable = True;

  set_quote_type (0);
  if (preselect_quote_style != NIL_PTR) {
	set_quote_style (preselect_quote_style);
  }

  if (file == NIL_PTR) {
	if (rpipe == False) {
		status_msg ("No file");
		empty = True;
	} else {
		fd = 0;
		file = "standard input";
	}
	file_name [0] = '\0';
  } else {
	update_file_name (file, False);
	status_line ("Accessing ", file);
	if (access (file, 0 /* F_OK */) < 0) {	/* Cannot access file */
		status_line ("New file ", file);
		overwriteOK = False;
		empty = True;
	} else if ((fd = open (file, O_RDWR | O_BINARY, 0)) >= 0) {
		overwriteOK = True;
		writable = True;
/*
		if (lockf (fd, F_LOCK, 0) < 0) {
			status_line ("No lock for ", file);
		} else {
			locked = True;
		}
*/
		if (open_linum == 0) {
			get_open_pos (file_name);
		}
	} else if ((fd = open (file, O_RDONLY | O_BINARY, 0)) >= 0) {
		overwriteOK = True;
		writable = False;
		if (open_linum == 0) {
			get_open_pos (file_name);
		}
	} else {
		error2 ("Cannot open: " /*, file */, serror ());
		empty = True;
	}
  }
  set_file_type_flags ();

/* Read file */
  loading = True;		/* Loading file, so set flag */
  reset_get_line ();

  if (fd >= 0) {
	status_line ("Reading ", file);
	save_utf8_text = utf8_text;
	save_cjk_text = cjk_text;
	if (auto_detect_utf) {
		utf8_text = False;
		cjk_text = False;
	}

#ifdef debug_timing
	long time_get = 0;
	long time_check = 0;
	long time_line = 0;
	long time_count = 0;
#endif

	mark_time (time_get);
	while (line != NIL_LINE
		&& (ret = get_line (fd, text_buffer, & len)) != ERRORS)
	{
		elapsed_mark_time (time_get, time_check);
		if (ret == NO_LINE || ret == SPLIT_LINE) {
			new_return_type = lineend_NONE;
		} else if (ret == NUL_LINE) {
			new_return_type = lineend_NUL;
		} else {
			new_return_type = extract_lineend_type (text_buffer, len);
		}
		elapsed_mark_time (time_check, time_line);
		line = line_insert (line, text_buffer, len, new_return_type);
		elapsed_time (time_line);

		/* if editing buffer cannot be filled (out of memory), 
		   assure that file is not accidentally overwritten 
		   with truncated buffer version
		 */
		if (line == NIL_LINE) {
			* file_name = '\0';
		}

		mark_time (time_count);
		nr_of_bytes += (long) len;

		if (auto_detect_utf) {
			nr_of_chars += char_count (text_buffer);
			nr_of_utfchars += utf8_count (text_buffer);
			cjk_text = True;
			nr_of_cjkchars += char_count (text_buffer);
			cjk_text = False;
		} else if (utf8_text) {
			nr_of_chars += utf8_count (text_buffer);
		} else {
			nr_of_chars += char_count (text_buffer);
		}

		if (new_return_type == lineend_NONE) {
			nr_of_chars --;
			nr_of_utfchars --;
			nr_of_cjkchars --;
			nr_of_bytes --;
		}
		elapsed_mark_time (time_count, time_get);
	}
	elapsed_time (time_get);
	utf8_text = save_utf8_text;
	cjk_text = save_cjk_text;

#ifdef debug_timing
	printf ("get %ld, check %ld, line_insert %ld, count %ld\n", time_get, time_check, time_line, time_count);
#endif

	if (total_lines == 0 && line != NIL_LINE) {
		/* The file was empty */
		line = line_insert (line, "\n", 1, lineend_NONE);
	}
	clear_buffer (); /* Clear output buffer: out_count = 0; */
	cur_line = header->next;
	line_number = 0;
	if (locked == False) {
		(void) close (fd);
	}

	if (count_lineend_CRLF > count_lineend_LF) {
		default_lineend = lineend_CRLF;
	}
  } else {
	/* Just install an empty line */
	line = line_insert (line, "\n", 1, default_lineend);
  }


  /* auto-detection stuff */

  /* remember text encoding before auto-detection (for CJK char counting) */
  prev_encoding_tag = text_encoding_tag;

  /* filter out encodings that shall not be auto-detected */
  if (detect_encodings != NIL_PTR && * detect_encodings != '\0') {
	if (strchr (detect_encodings, 'G') == NIL_PTR) {
		count_gb = 0;
	}
	if (strchr (detect_encodings, 'B') == NIL_PTR) {
		count_big5 = 0;
	}
	if (strchr (detect_encodings, 'K') == NIL_PTR) {
		count_uhc = 0;
	}
	if (strchr (detect_encodings, 'J') == NIL_PTR) {
		count_jp = 0;
	}
	if (strchr (detect_encodings, 'S') == NIL_PTR) {
		count_sjis = 0;
	}
	if (strchr (detect_encodings, 'C') == NIL_PTR) {
		count_cns = 0;
	}
	if (strchr (detect_encodings, 'H') == NIL_PTR) {
		count_johab = 0;
	}
	if (strchr (detect_encodings, 'V') == NIL_PTR) {
		count_good_viscii = 0;
	}
  }

  /* Unicode encoding detection */
  if (! empty && auto_detect_utf) {
	if (UTF8_BOM || 
		utf8_screen ? count_good_utf >= count_bad_utf
				: count_good_utf > count_bad_utf)
	{
		nr_of_chars = nr_of_utfchars;
		utf8_text = True;
		cjk_text = False;
		mapped_text = False;
	} else {
		utf8_text = False;
#ifdef debug_auto_detect
	printf ("good ISO %d, good VISCII %d, good CJK %d, weak CJK %d, bad CJK %d\n", 
		count_good_iso, count_good_viscii, count_good_cjk, count_weak_cjk, count_bad_cjk);
#endif

		if (count_good_viscii / 2 > count_good_cjk + count_weak_cjk
		    && count_good_viscii > count_good_iso * 1.2
		   ) {
			(void) set_text_encoding ("VISCII", 'V');
			detect_cjk = False;
		} else if (count_good_cjk > 
			count_bad_cjk * 2 + count_weak_cjk / 5
			/* count_bad_cjk * 2 + count_weak_cjk * 5 / nr_of_cjkchars */
		   && count_good_cjk > count_good_iso / 3
		   )
		{
			cjk_text = True;
			mapped_text = False;
			nr_of_chars = nr_of_cjkchars;
		} else {
			cjk_text = False;
		}
	}
  }

  /* detect CJK encoding based on 
     count_big5 count_gb count_uhc count_jp
  */
  if (! empty && cjk_text && detect_cjk) {
#ifdef debug_auto_detect
	printf ("big5 %d, gb %d, jp %d, sjis %d, uhc %d, johab %d\n", 
	count_big5, count_gb, count_jp, count_sjis, count_uhc, count_johab);
#endif
	count_big5 *= 2;
	count_uhc *= 2;
	count_jp *= 3;
	count_sjis *= 1.5;

#ifdef debug_auto_detect
	printf ("big5 %d, gb %d, jp %d, sjis %d, uhc %d, johab %d\n", 
	count_big5, count_gb, count_jp, count_sjis, count_uhc, count_johab);
#endif

	if (count_gb > count_big5 && count_gb > count_uhc && count_gb > count_jp && count_gb > count_sjis) {
		(void) set_text_encoding ("GB", 'G');
	} else if (count_uhc > count_big5 && count_uhc > count_gb && count_uhc > count_jp && count_uhc > count_sjis) {
		(void) set_text_encoding ("UHC", 'K');
	} else if (count_big5 > count_uhc && count_big5 > count_gb && count_big5 > count_jp && count_big5 > count_sjis) {
		(void) set_text_encoding ("Big5", 'B');
	} else if (count_jp > count_sjis) {
		(void) set_text_encoding ("EUC-JP", 'J');
	} else if (count_sjis > 0) {
		(void) set_text_encoding ("Shift-JIS", 'S');
	}
  }

  /* detect style of quotation marks */
  if (quote_type == 0) {
	determine_quote_style ();
  }

  /* if text encoding changed, reset previous one, then toggle */
  if (! streq (prev_text_encoding, get_text_encoding ())) {
	char * new_text_encoding = get_text_encoding ();
	set_text_encoding (prev_text_encoding, ' ');
	change_encoding (new_text_encoding);
  }

  /* end auto-detection stuff */


  if (fd >= 0) {
	if (line != NIL_LINE) {
	   show_get_l_errors ();
	   fstatus ("Read", nr_of_bytes, nr_of_chars);
	}
  }

  if (line == NIL_LINE) {
	sleep (2) /* give time to read allocation error msg */;
	viewonly = True;
	modified = False;
  }

  reset (header->next, 0);	/* Initialize pointers */
  move_to (0, 0);
  loading = False;		/* Stop loading, reset flag */

  if (open_linum > 0) {
	LINE * open_line = proceed (header->next, open_linum - 1);
	char * cpoi;
	char * prev_cpoi;
	int cur_column = 0;

	move_to (0, find_y_w_o_RD (open_line));

	cpoi = cur_line->text;
	prev_cpoi = cpoi;
	while (* cpoi != '\n' && cur_column <= open_col) {
		prev_cpoi = cpoi;
		advance_char_scr (& cpoi, & cur_column, cur_line->text);
	}
	if (cur_column <= open_col) {
		prev_cpoi = cpoi;
	}
	move_address (prev_cpoi, y);

	if (with_display) {
		display (0, top_line, last_y, 0);
	}
	if (! cjk_text || prev_encoding_tag == text_encoding_tag) {
		fstatus ("Read", nr_of_bytes, nr_of_chars);
	} else {
		fstatus ("Read", nr_of_bytes, -1L);
	}
	Markn (0);
  } else if (with_display) {
	display (0, top_line, last_y, 0);
	move_to (0, 0);
  }

#ifdef unix
  RD_window_title ();
#endif
}

static
void
load_file (file, with_display)
  char * file;
  FLAG with_display;
{
  load_file_position (file, with_display, 0, 0);
}


#ifdef msdos
# ifdef __TURBOC__
#include "mineddir.h"
# else
#include "dir.h"
# endif
struct ffblk ffblock;
FLAG ffblockvalid = False;
char wildfile [maxLINE_LEN];
char drive [9], dir [maxLINE_LEN], name [13], ext [5];
#endif

static
void
load_wild_file (file, with_display)
  char * file;
  FLAG with_display;
{
#ifdef msdos
  char dumdrive [9];
  char dumdir [maxLINE_LEN];
#endif

#ifdef msdos
  ffblockvalid = False;
#endif
  if (file == NIL_PTR) {
	load_file (file, with_display);
  } else {
#ifdef msdos
/*	(void) fnsplit (fnamv [fnami], drive, dir, name, ext);
	if (findfirst (fnamv [fnami], & ffblock, 0) == 0) {
*/
	(void) fnsplit (file, drive, dir, name, ext);
	if (findfirst (file, & ffblock, 0) == 0) {
		ffblockvalid = True;
		(void) fnsplit (ffblock.ff_name, dumdrive, dumdir, name, ext);
		fnmerge (wildfile, drive, dir, name, ext);
		load_file (wildfile, with_display);
	} else
#endif
		load_file (file, with_display);
  }
}


/*
 * Ask user if named file should be overwritten.
 */
FLAG
checkoverwrite (name)
  char * name;
{
  character c;

  status_line ("Checking ", name);
  if (access (name, 0 /* F_OK */) < 0) {	/* Cannot access file */
	return True;	/* thus no danger of unwanted damage */
  }

  status_line (name [0] ? name : empty_buffer_name,
			": OK to overwrite? (y/n)");
  c = promptyn ();
  clear_status ();
  if (c == 'y') {
	return True;
  } else if (c == 'n') {
	return False;
  } else {
/*	quit = False;	abort character has been given */
	return False;
  }
}

/*
 * Attach new file name to buffer
 */
void
NN ()
{
  char file [maxLINE_LEN];	/* Buffer for new file name */

  if (restricted) {
	restrictederr ();
	return;
  }
  if (get_filename ("Enter new file name:", file) == ERRORS) {
	return;
  }

  overwriteOK = False;
  writable = True;
  update_file_name (file, True);
#ifdef unix
  if (modified) {
	RD_window_title ();
  }
#endif
  set_modified ();	/* cf. CHDI command */
  clear_status ();
}


/*
 * writechar does a buffered output to file.
   (see also putoutchar which does the same)
 */
int
writechar (fd, c)
  int fd;
  char c;
{
  screen [out_count ++] = c;
  if (out_count == screen_BUFL) {
	return flush_buffer (fd);
  }
  return FINE;
}

/*
 * writeucs writes a UCS Unicode code in UTF-16
 * Return # words written or ERRORS.
 */
static
int
writeucs (fd, c)
  int fd;
  unsigned long c;
{
  int err = FINE;

  if (c > (unsigned long) 0x10FFFF) {
	return writeucs (fd, 0xFFFD);
  } else if (c > (unsigned long) 0xFFFF) {
	err = 2;
	c -= 0x10000;
	err |= writeucs (fd, 0xD800 | (c >> 10));
	err |= writeucs (fd, 0xDC00 | (c & 0x03FF));
  } else {
	err = 1;
	if (Unicode_little_endian) {
		err |= writechar (fd, c & 0xFF);
		err |= writechar (fd, c >> 8);
	} else {
		err |= writechar (fd, c >> 8);
		err |= writechar (fd, c & 0xFF);
	}
  }

  return err;
}

/*
 * writelechar writes a line-end character to file
 */
static
int
writelechar (fd, c)
  int fd;
  char c;
{
  if (utf8_text && Unicode_BOM) {
	return writeucs (fd, (character) c);
  } else {
	return writechar (fd, c);
  }
}

int
write_lineend (fd, return_type)
  register int fd;
  lineend_type return_type;
{
  switch (return_type) {
    case lineend_NONE:	return 0;
    case lineend_NUL:	if (writelechar (fd, '\0') == ERRORS) {
				return ERRORS;
			}
			return 1;
    case lineend_LF:	if (writelechar (fd, '\n') == ERRORS) {
				return ERRORS;
			}
			return 1;
    case lineend_CRLF:	if (writelechar (fd, '\r') == ERRORS) {
				return ERRORS;
			}
			if (writelechar (fd, '\n') == ERRORS) {
				return ERRORS;
			}
			return 2;
    case lineend_CR:	if (writelechar (fd, '\r') == ERRORS) {
				return ERRORS;
			}
			return 1;
    case lineend_LS:	/* Unicode line separator 2028:   */
			if (utf8_text && Unicode_BOM) {
				if (writeucs (fd, 0x2028) == ERRORS) {
					return ERRORS;
				}
				return 1;
			} else {
				if (writelechar (fd, '\342') == ERRORS) {
					return ERRORS;
				}
				if (writelechar (fd, '\200') == ERRORS) {
					return ERRORS;
				}
				if (writelechar (fd, '\250') == ERRORS) {
					return ERRORS;
				}
				return 3;
			}
    case lineend_PS:	/* Unicode paragraph separator 2029:   */
			if (utf8_text && Unicode_BOM) {
				if (writeucs (fd, 0x2029) == ERRORS) {
					return ERRORS;
				}
				return 1;
			} else {
				if (writelechar (fd, '\342') == ERRORS) {
					return ERRORS;
				}
				if (writelechar (fd, '\200') == ERRORS) {
					return ERRORS;
				}
				if (writelechar (fd, '\251') == ERRORS) {
					return ERRORS;
				}
				return 3;
			}
    default:		if (writelechar (fd, '\n') == ERRORS) {
				return ERRORS;
			}
			return 1;
  }
}

/*
 * Writestring writes the given string on the given filedescriptor.
 * (buffered via writechar via misused screen buffer!)
 * Return # bytes written or ERRORS.
 */
static
int
writestring (fd, text, return_type)
  int fd;
  char * text;
  lineend_type return_type;
{
  int len;
  int ccount = 0;

  while (* text != '\0') {
	if (* text == '\n') {
		/* handle different line ends */
		len = write_lineend (fd, return_type);
		if (len == ERRORS) {
			return ERRORS;
		}
		text ++;
		ccount += len;
	} else {
		if (utf8_text && Unicode_BOM) {
			unsigned long unichar;
			int utflen;
			utf8_info (text, & utflen, & unichar);
			if (UTF8_len (* text) == utflen) {
				len = writeucs (fd, unichar);
			} else {
				len = writeucs (fd, 0xFFFD);
			}
			if (len == ERRORS) {
				return ERRORS;
			}
			advance_utf8 (& text);
			ccount += len;
		} else {
			if (writechar (fd, * text) == ERRORS) {
				return ERRORS;
			}
			text ++;
			ccount ++;
		}
	}
  }

  if (utf8_text && Unicode_BOM) {
	return ccount * 2;
  } else {
	return ccount;
  }
}

/* Call graph for writing functions:
	panic --\------> panicwrite --------------------------\
		 > QUED ----------\			       \
	ESC q --/		   > ask_save --\		> write_file
	ESC e ---> EDIT --> edit_file -/	 \	       /
	ESC v ---> VIEW -/			  \	      /
	ESC w -------------------> WT -------------> write_text
	ESC W -------------------> WTU -----------/
	ESC z -------------------> SUSP ---------/
	ESC ESC -> EXED ---------> EXFILE ------/
			\--------> EXMINED ----/
	ESC t -------------------> Stag ------/
*/
long write_count;	/* number of bytes written */
long chars_written;	/* number of chars written */

/*
 * Write text in memory to file.
 */
static
void
write_file (fd)
  int fd;
{
  register LINE * line;
  int ret = FINE;

  write_count = 0L;
  chars_written = 0L;
  clear_buffer (); /* out_count = 0; */

  if (utf8_text && Unicode_BOM) {
	/* for UTF-16, assure file begins with a BOM */
	if (strncmp (header->next->text, "﻿", 3) != 0) {
		ret = writestring (fd, "﻿", lineend_NONE);
		if (ret == ERRORS) {
			error2 ("Write failed (File incomplete): ", serror ());
			write_count = -1L;
			chars_written = -1L;
		} else {
			ret = FINE;
			write_count = 2;
			chars_written = 1;
		}
	}
  }

  if (ret == FINE) {
    for (line = header->next; line != tail; line = line->next) {
	ret = writestring (fd, line->text, line->return_type);
	if (ret == ERRORS) {
		error2 ("Write failed (File incomplete): ", serror ());
		write_count = -1L;
		chars_written = -1L;
		break;
	}
	write_count += (long) ret;
	chars_written += (long) char_count (line->text);
	if (line->return_type == lineend_NONE) {
		chars_written --;
	}
    }
  }

  if (write_count > 0L && flush_buffer (fd) == ERRORS) {
	if (ret != ERRORS) {
		error2 ("Write failed (File incomplete): ", serror ());
		ret = ERRORS;
	}
	write_count = -1L;
	chars_written = -1L;
  }

  if (close (fd) == -1) {
	if (ret != ERRORS) {
		error2 ("Write failed (File incomplete): ", serror ());
	}
	write_count = -1L;
	chars_written = -1L;
  }
}

static
int
write_text (forceflag)
  FLAG forceflag;
{
  char file [maxLINE_LEN];	/* Buffer for new file name */
  int fd;			/* Filedescriptor of file */
  int ret;

  if (wpipe) {
    fd = STD_OUT;
    status_line ("Writing ", "to standard output");
    wpipe = False; /* no further write to same stream possible */
  } else {
    if (forceflag == False && modified == False) {
	if (file_name [0] != '\0') {
		fstatus ("(Write not necessary)", -1L, -1L);
		(void) save_open_pos (file_name, hop_flag);
	} else {
		status_msg ("Write not necessary.");
	}
	return FINE;
    }

    /* Check if file_name is valid and if file can be written */
    if (file_name [0] == '\0' || writable == False) {
	overwriteOK = False;
	if ((ret = get_filename ("Saving edited text - enter file name:", file))
		!= FINE)
	{
		return ret;
	}
	update_file_name (file, True);
#ifdef unix
	RD_window_title ();
#endif
    }
    if (overwriteOK == False) {
	if (checkoverwrite (file_name)) {
		overwriteOK = True;
	} else {
		if (quit == False) {
			writable = False;
		}
		return ERRORS;
	}
    }
    status_line ("Opening ", file_name);
/*    fd = creat (file_name, fprot | xprot);	*/
    fd = open (file_name, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, fprot | xprot);
    if (fd < 0) {	/* Empty file */
	error2 ("Cannot create or write: " /*, file_name */, serror ());
	writable = False;
	return ERRORS;
    } else {
	writable = True;
    }

    status_line ("Writing ", file_name);
  }

  write_file (fd);

  if (write_count == -1L) {
	return ERRORS;
  }

  modified = False;
#ifdef unix
  RD_window_title ();
#endif
  rpipe = False;	/* File name is now assigned */

/* Display how many chars (and lines) were written */
  fstatus ("Wrote", write_count, chars_written);
/*  fstatus ("Wrote", -1L); */
  (void) save_open_pos (file_name, hop_flag);
  return FINE;
}

FLAG
save_text_load_file (fn)
  char * fn;
{
	if (modified) {
		if (write_text (False) == ERRORS) {
			return ERRORS;
		}
	}

	/* Free old linked list, initialize global variables and load new file */
	initialize ();
	clearscreen ();
	load_file (fn, True);
	return FINE;
}

void
SAVEAS ()
{
  if (restricted) {
	restrictederr ();
	return;
  }
  NN ();
  WT ();
}

void
WT ()
{
  (void) write_text (False);
}

void
WTU ()
{
  if (restricted && viewonly) {
	restrictederr ();
	return;
  }
  (void) write_text (True);
}

/*
 * Save current file pos
 */
void
SAVPOS ()
{
  if (file_name [0] != '\0') {
	fstatus ("Remembering file position", -1L, -1L);
	if (save_open_pos (file_name, 1) == False) {
		error2 ("Error when saving file position to ", mark_file);
	}
  }
}

/*
 * Ask the user if he wants to save the file or not.
 */
static
int
ask_save ()
{
  register character c;

  status_line (file_name [0] ? file_name : empty_buffer_name,
			" has been modified. Save? (y/n)");
  c = promptyn ();
  clear_status ();
  if (c == 'y') {
	return write_text (False);
  } else if (c == 'n') {
	return FINE;
  } else {
	quit = False;	/* abort character has been given */
	return ERRORS;
  }
}

int
panicwrite ()
{
  int fd;
/*  fd = creat (panic_file, fprot);	*/
  fd = open (panic_file, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, bufprot);
  write_file (fd);
  if (write_count == -1L) {
	return ERRORS;
  } else {
	return FINE;
  }
}

/*
 * Edit/view another file. If the current file has been modified, 
 * ask whether the user wants to save it.
 * (We could allow to switch between edit and view mode without changing 
 * the file, but we would have to consider carefully the relationship 
 * between viewonly and modified.)
 */
static
void
edit_file (prompt, vomode)
  char * prompt;
  FLAG vomode;
{
  char new_file [maxLINE_LEN];	/* Buffer to hold new file name */

  if (modified && viewonly == False && ask_save () != FINE) {
	return;
  }

/* Get new file name */
  if (get_filename (prompt, new_file) == ERRORS) {
	return;
  }

  viewonly = vomode;

/* Free old linked list, initialize global variables and load new file */
  initialize ();
  clearscreen ();
  load_wild_file (new_file [0] == '\0' ? NIL_PTR : new_file, True);
}

#ifdef unused
static
void
edit_this_file (new_file, vomode)
  char * new_file;
  FLAG vomode;
{
  if (modified && viewonly == False && ask_save () != FINE) {
	return;
  }

  viewonly = vomode;

/* Free old linked list, initialize global variables and load new file */
  initialize ();
  clearscreen ();
  load_wild_file (new_file [0] == '\0' ? NIL_PTR : new_file, True);
}
#endif

void
EDIT ()
{
  if (restricted) {
	restrictederr ();
	return;
  }
  edit_file ("Edit file:", False);
}

void
VIEW ()
{
  if (restricted) {
	restrictederr ();
	return;
  }
  edit_file ("View file:", True);
}

void
EDITmode ()
{
  if (restricted) {
	restrictederr ();
	return;
  }
  viewonly = False;
  FSTAT ();
  flags_changed = True;
}

void
VIEWmode ()
{
  if (modified == False) {
	viewonly = True;
	FSTAT ();
	flags_changed = True;
  } else {
	error ("Cannot view only - already modified");
  }
}

void
toggle_VIEWmode ()
{
  if (viewonly) {
	EDITmode ();
  } else {
	VIEWmode ();
  }
}

static
void
quit_mined ()
{
  if (viewing_help) {
	end_view_help ();
	return;
  }

  delete_yank_files ();

  clear_status ();
  set_cursor (0, YMAX);
  putchar ('\n');
#ifdef unix
  clear_window_title ();
#endif
  flush ();
  raw_mode (False);
  exit (0);
}

void
edit_nth_file (n)
  int n;
{
  int number;
  int index;

  if (modified && viewonly == False && ask_save () != FINE) {
	return;
  }

  if (n == -1) {
	status_msg ("Edit which file (enter number) or # to reload current file");
	index = readcharacter ();
	if (quit) {
		return;
	}
	if (index == '#') {
		n = fnami;
	} else {
		index = get_number ("Edit which file (enter number) ...", index, & number);
		if (index == ERRORS) {
			return;
		}
		n = number - 1 + fnami_min;
	}
  }
  if (n < fnami_min) {
	n = fnami_min;
  }
  if (n > fnami_max) {
	n = fnami_max;
  }

  /* Now proceed to load nth file */
  viewonly = init_viewonly;
  /* Free old linked list, initialize global variables and load new file */
  initialize ();
  clearscreen ();

  fnami = n;
  if (fnami < fnami_min) {
	load_wild_file (NIL_PTR, True);
  } else {
	load_wild_file (fnamv [fnami], True);
  }
}

static
void
nextfile (exitiflast)
	FLAG exitiflast;
{
#ifdef msdos
  char dumdrive [9];
  char dumdir [maxLINE_LEN];
#endif

  if (hop_flag > 0) {
	edit_nth_file (fnami_max);
  } else {
#ifdef msdos
	if (modified && viewonly == False && ask_save () != FINE) {
		return;
	}

	if (ffblockvalid && findnext (& ffblock) == 0) {
		/* Discard edit buffer, initialize and load new file */
		initialize ();
		clearscreen ();
		(void) fnsplit (ffblock.ff_name, dumdrive, dumdir, name, ext);
		fnmerge (wildfile, drive, dir, name, ext);
		load_file (wildfile, True);
		return;
	}
	ffblockvalid = False;
#endif
	if (fnami >= fnami_max || fnami == 0) {
		if (exitiflast) {
			quit_mined ();
		} else {
			error ("Already at last file");
		}
	} else {
		edit_nth_file (fnami + 1);
	}
  }
}

void
NXTFILE ()
{
  Pushmark ();

  nextfile (False);
}

void
PRVFILE ()
{
  Pushmark ();

  if (hop_flag > 0) {
	edit_nth_file (fnami_min);
  } else if (fnami <= fnami_min) {
#ifdef msdos
		error ("Already at first file name");
#else
		error ("Already at first file");
#endif
		return;
  } else {
	edit_nth_file (fnami - 1);
  }
}

void
NTHFILE ()
{
  edit_nth_file (-1);
}

/*
 * Leave editor. If the file has changed, ask if the user wants to save it.
 */
void
QUED ()
{
  if (modified && viewonly == False && ask_save () != FINE) {
	return;
  }

  quit_mined ();
}

/*
 * Exit editing current file. If the file has changed, save it.
 * Edit next file if there is one.
 */
void
EXFILE ()
{
  if (modified) {
	if (write_text (False) != FINE) {
		return;
	}
  }

  if (hop_flag == 0) {
	nextfile (True);
  } else {
	quit_mined ();
  }
}

/*
 * Exit editor. If the file has changed, save it.
 */
void
EXMINED ()
{
  if (modified) {
	if (write_text (False) != FINE) {
		return;
	}
  }

  quit_mined ();
}

/*
 * Exit editing current file. Exit editor if multiexit flag set.
 */
void
EXED ()
{
  if (multiexit) {
	EXFILE ();
  } else {
	EXMINED ();
  }
}


/*
 * Display a line telling how many chars and lines the file contains. Also tell
 * whether the file is readonly and/or modified.
called via fstatus macro:
fstatus (mess, bytes, chars) is
	file_status ((mess), (bytes), (chars), file_name, \
		     total_lines, True, writable, modified, viewonly)
also called directly from WB
*/
void
file_status (message, bytecount, charcount, filename, lines, textstat, writefl, changed, viewing)
  char * message;
  register long bytecount;	/* Contains number of bytes in file */
  register long charcount;	/* Contains number of characters in file */
  char * filename;
  int lines;
  FLAG textstat;
  FLAG writefl;
  FLAG changed;
  FLAG viewing;
{
  register LINE * line;
  register int line_num = 0;
  static char msg [maxLINE_LEN + 40];	/* Buffer to hold line */
#ifdef buffer_info
  char yank_msg [maxLINE_LEN];	/* Buffer for msg of yank_file */
#endif

  /* determine information */
  if (bytecount < 0) {	/* # chars not given; count them */
	bytecount = 0L;
	charcount = 0L;
	for (line = header->next; line != tail; line = line->next) {
		bytecount += length_of (line->text);
		charcount += char_count (line->text);
		if (line->return_type == lineend_NONE) {
			charcount --;
			bytecount --;
		} else if (line->return_type == lineend_CRLF) {
			bytecount ++;
		} else if (line->return_type == lineend_LS
			|| line->return_type == lineend_PS) {
			bytecount += 2;
		}
		line_num ++;
		if (line == cur_line) {
			line_number = line_num;
		}
	}
  } else {		/* check for current line # only */
	for (line = header->next; line != cur_line->next; line = line->next) {
		line_num ++;
		if (line == cur_line) {
			line_number = line_num;
		}
	}
  }

#ifdef buffer_info
  if (yank_status == VALID && textstat) {
	/* Append buffer info */
	/* build_string (yank_msg, " Buffer: %ld char%s.", chars_saved,
					(chars_saved == 1L) ? "" : "s");
	*/
	yank_msg [0] = '\0';
	/* Empty paste buffer is only an initial condition and thus this 
	   would be no significant information; since buffer contents 
	   may be appended, the exact count is not available anyway. */
  } else {
	yank_msg [0] = '\0';
  }
#endif

  if (textstat) {
	build_string (msg,
		(lines_per_page != 0)
		? "%s %s%s%s%s, line %d of %d (chars/bytes: %ld/%ld), page %d"
		: "%s %s%s%s%s, line %d of %d (chars/bytes: %ld/%ld)",
		message,
		(rpipe && * message != '[') ?
			"standard input" : filename,
		viewing ? " (View only)" : "",
		changed ? " (modified)" : "",
		writefl ? "" : " (Readonly)",
		line_number,
		lines,
		charcount,
		bytecount,
		(lines_per_page != 0)
			? (line_number - 1) / lines_per_page + 1
			: 1
#ifdef buffer_info
		, yank_msg
#endif
		);
  } else {
	build_string (msg,
		"%s %s%s%s%s, lines: %d (chars/bytes: %ld/%ld)",
		message,
		(rpipe && * message != '[') ?
			"standard input" : filename,
		viewing ? " (View only)" : "",
		changed ? " (modified)" : "",
		writefl ? "" : " (Readonly)",
		lines,
		charcount,
		bytecount
		);
  }

  /* don't use status_msg here, msg contains filename characters */
  status_line (msg, NIL_PTR);	/* display the information */
}


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