static char rcsid[] = "@(#)$Id: curses.c,v 1.26 2006/07/01 08:27:54 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.26 $   $State: Exp $
 *
 *  Modified by: Kari Hurtta <hurtta+elm@posti.FMI.FI> 
 *                           (was hurtta+elm@ozone.FMI.FI)
 *****************************************************************************
 *
 * Some code copied from ../curses.c. It have following copyright:
 *
 * 			Copyright (c) 1988-1992 USENET Community Trust
 * 			Copyright (c) 1986,1987 Dave Taylor
 *****************************************************************************/


#include "def_screen.h"


DEBUG_VAR(Debug,__FILE__,"screen");

#ifdef TERMIOS
# ifndef sun
#  include <sys/ioctl.h>	/* for TIOCGWINSZ */
# endif
#else
# ifdef TERMIO
#  include <termio.h>
# else
#  include <sgtty.h>
# endif
#endif

#include    <errno.h>



/* Lose 'const' */
static char * cs2s P_((const char *str));
static char * cs2s(str)
     CONST char *str;
{

    return (char *)str;
}

static struct pg_modes {
    int mode;
    char * on;
    char * off;

    CONST char *set_on;
    CONST char *set_off;

} set_modes[] = {

    /* Possible incorrect -- was originally _setbold, _clearbold */
    { pg_BOLD, NULL, NULL,   
      "so"     /* Start standout mode */  ,
      "se"     /* End standout mode   */ },

    { pg_UNDERLINE, NULL, NULL,
      "us"     /* Start underlining   */  ,
      "ue"     /* End underlining     */ },


    { pg_HALFBRIGHT, NULL, NULL,
      "hs"     /* ... not listed ...  */  ,
      "he"     /* ... not listed ...  */ },

    { pg_INVERSE, NULL, NULL,
      "so"     /* Start standout mode */  ,
      "se"     /* End standout mode   */ }
};

static
char *_clearscreen = NULL, *_moveto = NULL, *_up = NULL, *_down = NULL, 
    *_right = NULL, *_left = NULL, 
    *_cleartoeoln = NULL, *_cleartoeos = NULL, *_set_memlock = NULL, 
    *_clear_memlock = NULL, 
    *_start_termcap = NULL, *_end_termcap = NULL, *_bell = NULL;

char *_transmit_on = NULL,  *_transmit_off = NULL; 

char *_key_up = NULL, *_key_down = NULL, *_key_left = NULL, *_key_right = NULL,
    *_key_pageup = NULL, *_key_pagedown = NULL, *_key_home = NULL, 
    *_key_help = NULL, *_key_find = NULL, *_key_backspace = NULL,
    *_key_delete = NULL, *_key_end = NULL;

/* Some additional capacities not used in ../src/curses.c */

static 
char *_up_many = NULL, *_down_many = NULL, *_left_many = NULL,
    *_right_many = NULL;


#if 0  /* SCROLL */
static char *_set_scroll_region = NULL, *_scroll_down = NULL;

static char *_scroll_up = NULL, *_scroll_up_many = NULL;
#endif

static int _lines, _columns, _automargin, _eatnewlineglitch;

int _intransmit = -1;	        /* are we transmitting keys? */

char terminal_type[40] = "";
static char _terminal[1024];              /* Storage for terminal entry */
static char _capabilities[1024];           /* String for cursor motion */

static char *ptr = _capabilities;	/* for buffering         */

static VOLATILE int _line  = -1;       /* initialize to "trash" */
static VOLATILE int _col   = -1;

int  cursor_control    = 0;
int cur_tabspacing             = 8;   /* Default? */

/* find tab stops preceding or following a given column position 'a', where
 * the column position starts counting from 1, NOT 0!
 */
#define prev_tab(a)	(((((a-1)/cur_tabspacing))*cur_tabspacing)+1)
#define next_tab(a)	(((((a-1)/cur_tabspacing)+1)*cur_tabspacing)+1)


#define DEFAULT_LINES_ON_TERMINAL	24
#define DEFAULT_COLUMNS_ON_TERMINAL	80


static char *us2s P_((unsigned char *str));
static char *us2s(str) 
     unsigned char *str;
{
    return (char *)str;
}

/* On SunOS 5.10  there is prototype
      int tputs(char *, int, int (*)(char))
   on /usr/include/term.h is __STDC__

   On other hand without prototype 
        int outchar(char)
   becomes 
        int outchar(int)

   This workaround seems not be required when compiling
   with gcc on sun

   On linux prototype is 
        int tputs(const char *str, int affcnt, int (*putc)(int));

*/

#if defined(__STDC__) && defined(sun) && !defined(__GNUC__)

int OUTCHAR(char c) 
{
    return outchar(c);
}

/* Also on SunOS 5.10 there is declaration for tgoto only
   if __STDC__ is NOT defined.

   When SunOS declares tgoto, following probably fails 
*/

extern char *tgoto(const char *cap, int col, int row);

#else

#define OUTCHAR  outchar
#endif


int cur_InitScreen()
{
    int i;
	/* Set up all this fun stuff: returns zero if all okay, or;
        -1 indicating no terminal name associated with this shell,
        -2..-n  No termcap for this terminal type known
   */


	int     err;
	char *termenv;
	int fd;

	if ((termenv = getenv("TERM")) == NULL) return(-1);

	strfcpy(terminal_type, termenv, sizeof terminal_type);

	if ((err = tgetent(_terminal, terminal_type)) != 1)
		return(err-2);
	
	/* _line, _col are asusmed to be trash -- do not
	   set them to zero now ... 
	*/

	fd = open("/dev/tty",O_RDWR);
	if (fd < 0) {
	    DPRINT(Debug,4, (&Debug, "Opening of /dev/tty failed\n"));
	} else {
	    FlushBuffer();
	    terminal_fd = fd;
	    DPRINT(Debug,4,(&Debug,
			    "/dev/tty is fd %d\n",terminal_fd));
	}
	    
	get_term_chars(terminal_fd);

	/* load in all those pesky values */
	_clearscreen       = tgetstr("cl", &ptr);     /*  Clear screen and cursor home */
	_moveto            = tgetstr("cm", &ptr);     /* Cursor move to row %1 and column %2 
							 (on screen) */
	_up                = tgetstr("up", &ptr);     /* Cursor up one line */
	_down              = tgetstr("do", &ptr);     /* Cursor down one line */
	_right             = tgetstr("nd", &ptr);     /* Cursor right one character */
	_left              = tgetstr("bc", &ptr);     /* backspace, if not ^H */
	_bell              = tgetstr("bl", &ptr);     /* Audio bell */

	
	for (i =0 ; i < (sizeof set_modes) / sizeof (set_modes[0]); i++) {
	    set_modes[i].on  = tgetstr(cs2s(set_modes[i].set_on),&ptr);
	    set_modes[i].off = tgetstr(cs2s(set_modes[i].set_off),&ptr);

	    switch(set_modes[i].mode) {

	    case pg_INVERSE:
		if (set_modes[i].on && set_modes[i].off) {
		    has_highlighting = TRUE;
		}
		break;
	    }

	}

	_cleartoeoln       = tgetstr("ce", &ptr);     /* Clear to end of line */
	_cleartoeos        = tgetstr("cd", &ptr);     /* Clear to end of screen */
	_lines	      	   = tgetnum("li");           /* Number of lines */
	_columns	   = tgetnum("co");           /* Number of columns */
	cur_tabspacing	   = ((cur_tabspacing=tgetnum("it"))<= 0 ? 8 : cur_tabspacing);
	                                              /* Difference between tab positions */
	_automargin	   = tgetflag("am");          /* Automatic margins which means automatic 
							 line wrap */
	_eatnewlineglitch   = tgetflag("xn");         /* Newline/wraparound glitch */
	_transmit_on	   = tgetstr("ks", &ptr);     /* Turn keypad on  */
	_transmit_off      = tgetstr("ke", &ptr);     /* turn keypad off */
	_set_memlock	   = tgetstr("ml", &ptr);     /* ... not listed ... */
	_clear_memlock	   = tgetstr("mu", &ptr);     /* ... not listed ... */
	_start_termcap	   = tgetstr("ti", &ptr);     /* Begin program that uses cursor motion */
	_end_termcap	   = tgetstr("te", &ptr);     /* End program that uses cursor motion */


	_key_up            = tgetstr("ku", &ptr);     /* Cursor up key */
	_key_down          = tgetstr("kd", &ptr);     /* Cursor down key */
	_key_left          = tgetstr("kl", &ptr);     /* Cursor left key */
	_key_right         = tgetstr("kr", &ptr);     /* Cursor right key */
	_key_pageup        = tgetstr("kP", &ptr);     /* Key for previous page */
	_key_pagedown      = tgetstr("kN", &ptr);     /* Key for next page */
	_key_home          = tgetstr("kh", &ptr);     /* Cursor home key */
	_key_help          = tgetstr("%1", &ptr);     /* help key */
	_key_find          = tgetstr("@0", &ptr);     /* find key */

	_key_backspace     = tgetstr("kb", &ptr);     /* backspace key */
	_key_delete        = tgetstr("kD", &ptr);     /* delete key */

	_key_end           = tgetstr("@7", &ptr);     /* PC end key */

	/* Linux termcap lists following .... 

	   mb   Start blinking
	   md   Start bold mode
	   me   End all mode like so, us, mb, md and mr
	   mh   Start half bright mode
	   mr   Start reverse mode
	   us   Start underlining
	   so   Start standout mode

	*/


        /* following strings are not copied from ../src/curses.c */

	_up_many           = tgetstr("UP", &ptr);    /* Cursor up %1 lines */
	_down_many         = tgetstr("DO", &ptr);    /* Cursor down %1 lines */
	_left_many         = tgetstr("LE", &ptr);    /* Cursor left %1 lines */
	_right_many        = tgetstr("RI", &ptr);    /* Cursor right %1 lines */

#if 0  /* SCROLL */
	_set_scroll_region = tgetstr("cs", &ptr);    /* Scroll region from 
						        line %1 to %2 */
	_scroll_down       = tgetstr("sr", &ptr);    /* Reverse scroll */

	_scroll_up         = tgetstr("sf", &ptr);    /* Normal scroll one line
							-- aka 'cursor down' */
	_scroll_up_many    = tgetstr("SF", &ptr);    /* Normal scroll n line
							-- aka 'cursor down' */

#endif

	if (_transmit_on && _transmit_off && _key_up && _key_down) {
	    cursor_control = TRUE;
	}


	if (!_left) {
		_left = "\b";
	}

	if (_lines == 0) _lines = DEFAULT_LINES_ON_TERMINAL;
	if (_columns == 0) _columns = DEFAULT_COLUMNS_ON_TERMINAL;

	return 0;
}

/* DO NOT return lines-1 (as ScreenSize() did) */

void cur_ScreenSize(lines, columns)
     int *lines, *columns;
{
	/** returns the number of lines and columns on the display. **/

#ifdef TIOCGWINSZ
	struct winsize w;

	if (ioctl(1,TIOCGWINSZ,&w) != -1) {
		if (w.ws_row > 0)
			_lines = w.ws_row;
		if (w.ws_col > 0)
			_columns = w.ws_col;
	}
#endif

	if (_lines == 0) _lines = DEFAULT_LINES_ON_TERMINAL;
	if (_columns == 0) _columns = DEFAULT_COLUMNS_ON_TERMINAL;



	*lines = _lines;	   /* This do not return lines-1 
				      as ScreenSize() did
				   */     
	*columns = _columns;
}

void cur_GetXYLocation(row,col)
     int *row,*col;
{
	/* return the current cursor location on the screen */

	*row = _line;
	*col = _col;
}

static int  need_moveabsolute = 0;

void InvalidateLocation()
{
    need_moveabsolute = 1;
    _line = 0;
    _col  = 0;
}

void cur_ClearScreen()
{
    /* clear the screen: returns -1 if not capable */

    _line = 0;	/* clear leaves us at top... */
    _col  = 0;

    if (default_context->redraw) {
	SIGDPRINT(Debug,7,(&Debug, 
			   "cur_ClearScreen: clearing redraw context from default context\n"));
	default_context->redraw  = 0;
    }
    _intransmit = -1;   /* Re-set state */

    if (!_clearscreen) {
	SIGDPRINT(Debug,4,(&Debug, "cur_ClearScreen: NO _clearscreen set!!\n"));

	return;
    }

    tputs(_clearscreen, 1, OUTCHAR);
    FlushBuffer();      /* clear the output buffer */
 
    return;
}

static void moveabsolute P_((int col, int row));

static void cur_CursorUp P_((int n));
static void cur_CursorUp(n)
     int n;
{
  /** move the cursor up 'n' lines **/
  /** Calling function must check that _up is not null before calling **/

  if (need_moveabsolute)
      moveabsolute(_col, _line);

  _line = (_line-n > 0? _line - n: 0);	/* up 'n' lines... */

  if (n > 1 && _up_many) {
      char *stuff;

      stuff = tgoto(_up_many,0,n);
      tputs(stuff, 1, OUTCHAR);
      
  } else while (n-- > 0)
      tputs(_up, 1, OUTCHAR);

}

static void cur_CursorDown P_((int n));
static void cur_CursorDown(n)
     int n;
{
  /** move the cursor down 'n' lines **/
  /** Caller must check that _down is not null before calling **/

  if (need_moveabsolute)
      moveabsolute(_col, _line);

  _line = (_line+n < _lines? _line + n: _lines-1);    /* down 'n' lines... */

  if (n > 1 &&	_down_many) {
      char *stuff;

      stuff = tgoto(_down_many,0,n);
      tputs(stuff, 1, OUTCHAR);

  } else while (n-- > 0)
      tputs(_down, 1, OUTCHAR);  
}


static void cur_CursorLeft P_((int n));
static void cur_CursorLeft(n)
     int n;
{
    /** move the cursor 'n' characters to the left **/
    /** Caller must check that _left is not null before calling **/
    
    if (need_moveabsolute)
	moveabsolute(_col, _line);
    
    _col = (_col - n> 0? _col - n: 0);	/* left 'n' chars... */
  
    if (n > 1 &&  _left_many) {
	char *stuff;
	
	stuff = tgoto(_left_many,0,n);
	tputs(stuff, 1, OUTCHAR);
	
    } else while (n-- > 0)
	tputs(_left, 1, OUTCHAR);
        
}

static void cur_CursorRight P_((int n));
static void cur_CursorRight(n)
     int n;
{
    /** move the cursor 'n' characters to the right (nondestructive) **/
    /** Caller must check that _right is not null before calling **/
    
    if (need_moveabsolute)
	moveabsolute(_col, _line);
    
    /* NOTE: if _col == _columns then it is outside of columns */

    _col = (_col+n < _columns? _col + n: _columns);	
    /* right 'n' chars... */
    
    if (n > 1 &&  _right_many) {
	char *stuff;
	
	stuff = tgoto(_right_many,0,n);
	tputs(stuff, 1, OUTCHAR);
	
    } else while (n-- > 0)
	tputs(_right, 1, OUTCHAR);
    
}

static void moveabsolute(col, row)
     int col, row;
{

    char *stuff;

    if (need_moveabsolute) {
	SIGDPRINT(Debug,4,(&Debug, 			   
			   "Curses: moveabsolute: Syncronizing cursos position (col=%d,row=%d)\n",
			   col,row));
    }
    
    stuff = tgoto(_moveto, col, row);
    tputs(stuff, 1, OUTCHAR);
     
    if(need_moveabsolute)
	FlushBuffer();
       
    need_moveabsolute = 0;
}

static int in_area P_((int line, struct cur_printarea *lim));
static int in_area(line,lim)
     int line; 
     struct cur_printarea *lim;
{
    int r = line >= lim->first_line && line <= lim->last_line;

    if (line >= _lines) {
	DPRINT(Debug,4, (&Debug, "in_area: Not in screen (line=%d)\n",line));
	r = 0;
    }

    DPRINT(Debug,12, (&Debug, 
		      "in_area(line=%d,lim=%p)=%d: first_line=%d last_line=%d\n",
		      line,lim,r,lim->first_line,lim->last_line));

    return r;
}

void cur_MoveCursor(row, col, lim)
     int row, col;
     struct cur_printarea *lim;

{
    /** move cursor to the specified row column on the screen.
	0,0 is the top left! **/
    
    int scrollafter = 0;

    /* we don't want to change "rows" or we'll mangle scrolling... */
    
    if (need_moveabsolute)
	moveabsolute(_col, _line);;
    
    if (lim) {
	int in = in_area(row,lim);
	

	if (lim->gone_out && in) {
	    DPRINT(Debug,4,(&Debug,"cur_MoveCursor: Going to printing area, enabling printing\n"));
	    lim->gone_out = 0;
	}

	if (!in) {

	    if ( ! lim->gone_out) {
		DPRINT(Debug,4,(&Debug,"cur_MoveCursor: Going out of printing area, disabling printing\n"));
		lim->gone_out = 1;
	    }

	    return;
	}

    }


    if (col < 0)
	col = 0;
    if (col >= _columns)
	col = _columns - 1;
    if (row < 0)
	row = 0;
    if (row >= _lines) {
	if (col == 0)
	    scrollafter = row - _lines +1;
	row = _lines -1;
    }

    if (!_moveto) {
	DPRINT(Debug,4,(&Debug, "curses: NO _moveto set!!\n"));

	return;
    }
    
    if (row == _line) {
	if (col == _col)
	    return;				/* already there! */
	
	else if (col == 0)
	    outchar('\r');

	else if (abs(col - _col) < 5     /* within 5 spaces... */  ||
		 _left_many && _right_many
		 ) {	
	    if (col > _col && _right)
		cur_CursorRight(col - _col);
	    else if (col < _col &&  _left)
		cur_CursorLeft(_col - col);
	    else
		moveabsolute(col, row);
	}
	else 		/* move along to the new x,y loc */
	    moveabsolute(col, row);
    }
    else if (_line == row-1 && col == 0) {
	if (_col != 0)
	    outchar('\r');
	outchar('\n');      
    }
    else if (col == _col && (abs(row - _line) < 5 ||
			     _up_many && _down_many
			     )) {
	if (row < _line && _up)
	    cur_CursorUp(_line - row);
	else if (row > _line && _down)
	    cur_CursorDown(row - _line);
	else
	    moveabsolute(col, row);
    }
    else 
	moveabsolute(col, row);
    
    _line = row;	/* to ensure we're really there... */
    _col  = col;
    
    if (scrollafter) {

	DPRINT(Debug,49, (&Debug, " scrollafter=%d\n",scrollafter));
	
	if (lim) {
	    DPRINT(Debug,4,(&Debug,"cur_MoveCursor: scroling not allowed, disabling printing\n"));
	    lim->gone_out = 0;

	    FlushBuffer();	
	    return;
	}

	outchar('\r');

	while (scrollafter--) {
	    outchar('\n');
	}
    }

    FlushBuffer();	
}

void cur_changemode(mode,set,reset)
     int *mode; 
     int set; 
     int reset;
{
    int i;

    /* To allow switching modes from 'reset' to 'set', run reset first */

    for (i =0 ; i < (sizeof set_modes) / sizeof (set_modes[0]); i++) {
	if ((reset & set_modes[i].mode)   && set_modes[i].off) {
	    tputs(set_modes[i].off, 1, OUTCHAR);
	    *mode &= ~(set_modes[i].mode);
	}
    }


    for (i =0 ; i < (sizeof set_modes) / sizeof (set_modes[0]); i++) {

	if ((set & set_modes[i].mode)   && set_modes[i].on) {
	    tputs(set_modes[i].on, 1, OUTCHAR);
	    *mode |= set_modes[i].mode;
	}

    }

}

/*  ----------------------------------------------------------- */

VOLATILE static int wrappedlastchar = 0;
static int tabexpand = 0;          /* Is terminal driver expanding tabs? */

/* called with value of -1 if just preprosessing needed 
   (ie need_moveabsolute) 

   returns value indication is character printing needed
*/
static int write_control P_((int ch, struct cur_printarea *lim));
static int write_control(ch,lim)
     int ch;
     struct cur_printarea *lim;
{
    /* Setting lim disables scrooling !!
     */

    int justwrapped, nt;
    static int warn = 0;

    if (need_moveabsolute)
	moveabsolute(_col, _line);

    justwrapped = 0;

    /* We do nothing if cursor is not currently on area ... */
    if (lim && !in_area(_line,lim)) {
	if (!warn) {
	    SIGDPRINT(Debug,4,(&Debug,
			       "write_control: Cursor not in print area (lines %d-%d): line %d\n",
			       lim->first_line,
			       lim->last_line,
			       _line));
	}
	warn++;
	return 0;
    }

    if (lim && lim->gone_out) {
	if (!warn) {
	    SIGDPRINT(Debug,4,(&Debug,
			       "write_control: Cursor was going out of print area (lines %d-%d): line %d\n",
			       lim->first_line,
			       lim->last_line,
			       _line));
	}
	warn++;
	return 0;
    }

    warn = 0;

    switch (ch) {
    /* if return, just go to left column. */
    case 0x0D: /* CR */

	if (wrappedlastchar)
	    justwrapped = 1;                /* preserve wrap flag */
	else {
	    outchar('\r');
	    _col = 0;
	}
	break;
    
	/* if newline and terminal just did a newline without our asking,
	 * do nothing, else output a newline and increment the line count */
    case 0x0A: /* LF */
	if (!wrappedlastchar) {

	    if (lim && _line == lim->last_line) {

		SIGDPRINT(Debug,4,(&Debug,
				   "write_control: going out of printing area (CR), disabling printing\n"));

		/* Disable printing */
		need_moveabsolute = 1;
		lim->gone_out = 1;
		return 0;
	    }

	    outchar('\n');
	    if (_line < _lines-1)
		++_line;
	}
	break;
    
    /* if backspace, move back  one space  if not already in column 0 */
    case 0x08:  /* BS */
	if (_col != 0) {
	    tputs(_left, 1, OUTCHAR);
	    _col--;
	}

	else if (_line > 0) {

	    if (lim && _line == lim->first_line) {

		/* BACKSPACE does nothing */
		break;
	    }

	    _col = _columns - 1;
	    _line--;
	    moveabsolute (_col, _line);
	}
	/* else BACKSPACE does nothing */
	break;

	/* if bell, ring the bell but don't advance the column */
    case 0x07: /* BEL */
	if (_bell)
	    tputs(_bell,1,OUTCHAR);
	else
	    outchar(ch);
	break;

    /* if a tab, output it */
    case 0x09: /* HT */
	/* If terminal driver is expanding tabs, don't trust it ... */
	if (!tabexpand)
	    outchar(ch);
	if((nt=next_tab(_col+1)) > prev_tab(_columns))
	    _col = _columns-1;
	else
	    _col = nt-1;
	if (tabexpand)
	    moveabsolute (_col, _line);
	break;
    default:    /* Is 'printable' character (or -1) */

	if (lim && _col == _columns-1 && _automargin && 
	    _line == _lines-1) {

	    SIGDPRINT(Debug,4,(&Debug,
			       "write_control: going out of printing area and screen, disabling printing on last column\n"));

	    /* Disable printing */
	    need_moveabsolute = 1;
	    lim->gone_out = 1;

	    return 0;
	}


	return 1;
    }
    
    wrappedlastchar = justwrapped;
    return 0;   /* Need no printing */
}

static void increment_position P_((int len,struct cur_printarea *lim));
static void increment_position(len,lim)
     int len;
     struct cur_printarea *lim;
{
    int justwrapped;

    justwrapped = 0;
    
    if (_col + len > _columns) {
	_col = 0;
	_line = 0;
	moveabsolute (_col, _line);
	panic("CURSES PANIC",__FILE__,__LINE__,"increment_position",
	      "column position overflow",0);
    }

    /* if we only had one column left, simulate automargins if
     * the terminal doesn't have them */
    if (_col + len == _columns) {

	if (lim && _col == _columns-1 && _line == lim->last_line) {
	    
	    SIGDPRINT(Debug,4,(&Debug,
			       "increment_position: going out of printing area (CR), disabling printing\n"));

	    /* Disable printing */
	    need_moveabsolute = 1;
	    lim->gone_out = 1;

	} else {

	    if (!_automargin || _eatnewlineglitch) {
		SIGDPRINT(Debug,40,(&Debug, "increment_position: EOLN -- printing CR LF\n"));
		outchar('\r');
		outchar('\n');
	    } else {
		SIGDPRINT(Debug,40,(&Debug, "increment_position: EOLN -- screen wrapping assumed\n"));
	    }
	    if (_line < _lines-1)
		++_line;
	    _col = 0;
	    justwrapped = 1;
	}
    }
    /* if we are here this means we have no interference from the
     * right margin - just increment the column position. */
    else {
	_col += len;
    }

    wrappedlastchar = justwrapped;
}

/* Called by signal handler ... */

void WriteRaw(str)
     CONST char *str;
{
     CONST char * p;


     /* If WriteRaw() is called, then screen must be redraw
	later ...
     */
     menu_context_redraw();

     for (p = str; *p; p++) {
	 int ch = *p;
	 
	 /** write a character to the current screen location. **/
	 int need_print = 1;

	 ch &= 0xFF;

	 need_print = write_control(ch, NULL);

	 if (need_print) {
	     outchar(ch);
	     increment_position(1, NULL);
	 }
     }

     FlushBuffer();
}



/* This assumes one byte charset */
void cur_Writechar(ch, lim)
     int ch;
     struct cur_printarea *lim;
{
    /** write a character to the current screen location. **/
    int need_print = 1;

    ch &= 0xFF;

    /* ASCII assumed */
    need_print = write_control(ch,lim);

    if (need_print) {
#ifndef ASCII_CTYPE
	if (display_charset == system_charset) {
	    /* if some kind of non-printable character change to a '?' */

	    if(!isprint((unsigned char)ch))
		ch = '?';
	} else 
#endif
	    {
		/* If system (= locale) charset is not display charset
		 * need ask from charset system is that character
		 * printable -- complex and expensive!
		 */
		struct charset_state *st = new_state(display_charset);
		add_streambyte_to_state(st,ch);
		
		if (!state_ready(st) || !state_printable(st))
		    ch = '?';		
		free_state(&st);
	    }
	outchar(ch);
	increment_position(1,lim);
    }
}


static int preinit_output P_((const struct string *S, struct string **buffer,
			      charset_t stored_display));
static int preinit_output(S,buffer, stored_display)
     CONST struct string *S;
     struct string **buffer;
     charset_t stored_display; 
{
    int sw = -1;

    if (S->string_type == display_charset)
	*buffer = dup_string(S);
    else if (target_display_charset == display_charset &&
	     S->string_type == system_charset) {
	sw = 1;  /* Arrange return to back to display charset */
	*buffer = dup_string(S);
	switch_display_charset(0); /* Print using system charset */
    } else if (allow_charset_switching && 
	       0 != (charset_properties(S->string_type) & CS_printable) &&
	       stored_display &&
	       set_display_charset(S->string_type,1)) {
	sw = 2;  /* Arrange return to correct display charset */
	*buffer = dup_string(S);
    } else
	*buffer = convert_string(display_charset,S,0);

    return sw;
}

static void postprocess_output P_((charset_t stored_display, int sw));

static void postprocess_output(stored_display,sw)
     charset_t stored_display; 
     int sw;
{
    if (sw == 2)
	set_display_charset(stored_display,0);
    else if (sw != -1) {
	switch_display_charset(sw);
    }    
}

struct string *curses_printable_clip(S,pos,len,visible_len,max_visible)
     CONST struct string *S;
     int *pos;
     int len;
     int *visible_len;
     int max_visible;
{
    struct string *buffer = NULL;
    charset_t stored_display = target_display_charset;
    int sw = -1;
 
    struct string *ret1   = NULL;

    sw =  preinit_output(S,&buffer, stored_display);

    DPRINT(Debug,35,(&Debug, 
		    "curses_printable_clip: S=%S cs=%s  -- using %s\n",
		     S,
		     S->string_type->MIME_name ? 
		     S->string_type->MIME_name : 
		     "<no MIME name>",
		     buffer->string_type->MIME_name ? 
		     buffer->string_type->MIME_name : "<no MIME name>"));
    DPRINT(Debug,35,(&Debug, "curses_printable_clip: len=%d max_visible=%d pos=%d\n",
		     len,max_visible,*pos));

    if (last_display_state && 
	0 != (CS_printable_len & charset_properties(buffer->string_type))) {

	int l1;
	struct cs_printable_len PRINTABLE_LEN;
	
	PRINTABLE_LEN.max_len = max_visible;
	PRINTABLE_LEN.ret_len = 0;

	l1 = estimate_clip_string(buffer,*pos,len,last_display_state,&PRINTABLE_LEN);

	DPRINT(Debug,35,(&Debug,
			 "curses_printable_clip: l1=%d printable_len { max_len %d ret_len %d }\n",
			 l1,PRINTABLE_LEN.max_len,PRINTABLE_LEN.ret_len));

	
	if (l1 < 0) {
	    ret1         = NULL;
	    *visible_len = -1;
	} else {
	    *visible_len = PRINTABLE_LEN.ret_len;
	    ret1 = clip_from_string(buffer,pos,l1);
	}
    } else {
	if (max_visible < len)
	    len = max_visible;

	ret1 = clip_from_string(buffer,pos,len);

	*visible_len = string_len(ret1);
    }

    /* Free if converted */
    if (buffer != S)
	free_string(&buffer);

    postprocess_output(stored_display,sw);

    if (ret1) {
	DPRINT(Debug,35,(&Debug, "curses_printable_clip=%p=%S visible_len=%d pos=%d\n",
			 ret1,ret1,*visible_len,*pos));
    }
    return ret1;
}

void cur_PutLineS(S, lim)
     struct string *S;
     struct cur_printarea *lim;
{
    int idx = 0;
    struct string *buffer = NULL;
    charset_t stored_display = target_display_charset;
    int sw = -1;
    int buffer_len;


    

    sw =  preinit_output(S,&buffer, stored_display);

    buffer_len = string_len(buffer);

    DPRINT(Debug,40,(&Debug, 
		    "PutLineS: S=%S cs=%s  -- using %s\n",
		     S,
		     S->string_type->MIME_name ? 
		     S->string_type->MIME_name : 
		     "<no MIME name>",
		     buffer->string_type->MIME_name ? 
		     buffer->string_type->MIME_name : "<no MIME name>"));
    
    while (idx < buffer_len) {
	char * s, *s1;
	uint16 ch;
	int old_idx = idx;
	int need_print;
	int len = 1;
	int q = 0;
	struct cs_printable_len PRINTABLE_LEN;
	struct cs_printable_len *printable_len = NULL;

	ch = give_unicode_from_string(buffer,idx);

	/* ASCII assumed */
	if (ch <= 31) 
	    need_print = write_control(ch, lim);
	else 
	    need_print = write_control(-1, lim);  /* HACK for position of cursor */
	if (!need_print) {

	    if (lim && !in_area(_line,lim)) {
		DPRINT(Debug,40,(&Debug,
				 "PutLineS: [%d] not in area ... \n",
				 idx));
		break;
	    }

	    DPRINT(Debug,40,(&Debug,
		       "PutLineS: [%d] unicode=%d (control)\n",
		       idx,ch));
	    idx++;
	    continue;   /* character 'printed' */
	}

	while (len < _columns-_col &&
	       len + idx < buffer_len &&
	       give_unicode_from_string(buffer,idx + len) > 31)
	    len++;

	if (last_display_state && 
	    0 != (CS_printable_len & 
		  charset_properties(buffer->string_type))) {

	    PRINTABLE_LEN.max_len = _columns-_col;
	    PRINTABLE_LEN.ret_len = 0;

	    printable_len = &PRINTABLE_LEN;
	}
	s = us2s(streamclip_from_string(buffer,&idx,len,
					last_display_state,printable_len));

	DPRINT(Debug,10,(&Debug,
			 "PutLineS: [%d-%d] stream=",
			 old_idx,idx-1,s));

	for (s1 = s; *s1; s1++) {
#ifdef DEBUG
	    if (isascii(*s1) && isprint(*s1) && '"' != *s1) {
		if (!q) { DPRINT(Debug,10,(&Debug," \"")); q = 1; }
		DPRINT(Debug,10,(&Debug,"%c",*s1));
	    } else {
		if (q) { DPRINT(Debug,10,(&Debug,"\"")); q = 0; }
		DPRINT(Debug,10,(&Debug," %02X", (unsigned char)*s1));
	    }
#endif
	}

	if (q) { DPRINT(Debug,10,(&Debug,"\"")); q = 0; }
	if (printable_len) {
	    DPRINT(Debug,10,(&Debug,
			     " printable_len { max_len %d ret_len %d }",
			     printable_len->max_len,printable_len->ret_len));
	}
	DPRINT(Debug,10,(&Debug,"\n"));

	for (s1 = s; *s1; s1++) {
	    outchar(*s1);
	}

	if (printable_len)
	    increment_position(printable_len->ret_len, lim);
	else
	    increment_position(idx-old_idx, lim);

	if (idx == old_idx) {
	    DPRINT(Debug,10,(&Debug,"No any characters printed...\n"));
	    CleartoEOLN();
	    cur_Writechar('\r',lim);
	    cur_Writechar('\n',lim);	    
	}

	free(s);
    }

    /* Free if converted */
    if (buffer != S)
	free_string(&buffer);

    postprocess_output(stored_display,sw);
}


void cur_PutLine0(row, col, line, lim)
     int row,col;
     CONST char *line;
     struct cur_printarea *lim;
{
    /** Write a zero argument line at location x,y **/

    if (lim && ! in_area(row,lim)) {
	DPRINT(Debug,10,(&Debug,
			 "cur_Putline0: (row %d,col %d) not in area row %d-%d\n",
			 row,col,
			 lim->first_line,
			 lim->last_line));
	
	lim->gone_out = 1;
	return;
    }
    if (lim)
	lim->gone_out = 0;

    cur_MoveCursor(row,col, lim);
    while(*line) {
	cur_Writechar(*line++, lim);    
	
	if (lim && !in_area(_line,lim)) {
		DPRINT(Debug,40,(&Debug,
				 "cur_Putline0: not in area ... \n"));
		break;
	}


    }
}

void cur_CleartoEOLN()
{
    /** clear to end of line **/
    
    if (need_moveabsolute)
	moveabsolute(_col, _line);
    
    if (!_cleartoeoln) {
	SIGDPRINT(Debug,4,(&Debug, "curses: NO _cleartoeoln set!!\n"));

	return;
    }

    
    tputs(_cleartoeoln, 1, OUTCHAR);
    
    return;
}


void cur_CleartoEOS()
{
    /** clear to end of screen **/
    
    if (need_moveabsolute)
	moveabsolute(_col, _line);
    
    if (!_cleartoeos) {
	SIGDPRINT(Debug,4,(&Debug, "curses: NO _cleartoeos set!!\n"));

	return;
    }
    
    tputs(_cleartoeos, 1, OUTCHAR);
    
    return;
}

static int _inraw = 0;                  /* are we IN rawmode?    */

int cur_RawState()
{
    /** returns either 1 or 0, for ON or OFF **/
    
    FlushBuffer();

    return( _inraw );
}

#ifdef PTEM
#  include <sys/stream.h>
#  include <sys/ptem.h>
#endif

#ifdef TERMIOS
struct termios _raw_tty,
	       _original_tty;
#define	ttgetattr(fd,where)	tcgetattr((fd),(where))
#define	ttsetattr(fd,where)	tcsetattr((fd),TCSADRAIN,(where))
#else	/*TERMIOS*/
# ifdef TERMIO
struct termio _raw_tty, 
              _original_tty;
#define	ttgetattr(fd,where)	ioctl((fd),TCGETA,(where))
#define	ttsetattr(fd,where)	ioctl((fd),TCSETAW,(where))
# else
struct tty_modes {
    struct sgttyb sgttyb;
    struct tchars tchars;
}  _raw_tty, _original_tty;
static int ttgetattr P_((int,struct tty_modes *));  /* Prototype */
static int ttsetattr P_((int,struct tty_modes *)); /* Prototype */
# endif	/*TERMIO*/
#endif	/*TERMIOS*/

/* NOTE: Is called from signal handler! */
void cur_Raw(state)
     int state;
{
    int do_tite     = (state & NO_TITE) == 0;
    int do_charset  = (state & NO_CHARSET) == 0;
    int sig         = (state & RAW_FROM_SIGNAL);

    state = state & ~NO_TITE & ~NO_CHARSET & ~RAW_FROM_SIGNAL;
    
    SIGDPRINT(Debug,4,(&Debug, 
		       "curses: Raw: state=%d do_tite=%d do_charset=%d sig=%d\n",
		       state,do_tite,do_charset, sig));

    /** state is either ON or OFF, as indicated by call **/

    if (state == OFF && _inraw) {
	SIGDPRINT(Debug,4,(&Debug,
			   "curses: Raw: Setting Raw state OFF\n"));;

#if POLL_METHOD	  
	clear_action(terminal_fd);
	clear_input_buffer();
#endif
	
	if (cursor_control)
	    transmit_functions(OFF);
	
	if (do_charset)
	    switch_display_charset(0);
	/* We do NOT call switch_title(0) here because 
	   raw state off may be called because running of program
	*/

	if (use_tite && _end_termcap && do_tite) {
	    tputs(_end_termcap, 1, OUTCHAR);	    
	}
	FlushBuffer();
	(void) ttsetattr(terminal_fd,&_original_tty);
	_inraw = 0;
    }
    else if (state == ON && ! _inraw) {

	SIGDPRINT(Debug,4,(&Debug,
			   "curses: Raw: Setting Raw state ON\n"));;
	    
	(void) ttgetattr(terminal_fd, &_original_tty);
	(void) ttgetattr(terminal_fd, &_raw_tty);    /** again! **/
	
#if !defined(TERMIO) && !defined(TERMIOS)
	_raw_tty.sg_flags &= ~(ECHO);	/* echo off */
	_raw_tty.sg_flags |= CBREAK;	/* raw on    */

#else
	_raw_tty.c_lflag &= ~(ICANON | ECHO);	/* noecho raw mode        */

	_raw_tty.c_cc[VMIN] = '\01';	/* minimum # of chars to queue    */
	_raw_tty.c_cc[VTIME] = '\0';	/* minimum time to wait for input */


#if defined(OXTABS)
	/* Some systems use OXTABS bit */
	if (_raw_tty.c_oflag & OXTABS) {
	    SIGDPRINT(Debug,4,(&Debug,
			       "curses: [OXTABS] Terminal driver is expanding tabs...\n"));
	    tabexpand = 1;
	} else {
	    if (tabexpand) 	      
		SIGDPRINT(Debug,4,(&Debug,
				   "curses: [OXTABS] Terminal driver isn't expanding tabs...\n"));
	    tabexpand = 0;
	}
#endif /* defined(OXTABS) */

#if defined(TABDLY) && !defined(OXTABS)
	/* Some systems expands tab when TABDLY is XTABS */
	if ((_raw_tty.c_oflag & TABDLY) == 
#ifdef XTABS
	    XTABS
#else
	    TAB3
#endif
	    ) {
	    SIGDPRINT(Debug,4,(&Debug,
			       "curses: [TABDLY] Terminal driver is expanding tabs...\n"));
	    tabexpand = 1;
	} else {
	    if (tabexpand) 
		SIGDPRINT(Debug,4,(&Debug,
				   "curses: [TABDLY] Terminal driver isn't expanding tabs...\n"));
	    tabexpand = 0;
	}
#endif /* defined(TABDLY) && !defined(OXTABS) */

#if !defined(TABDLY) && !defined(OXTABS)
	
	/* If _POSIX_SOURCE is defined then OXTABS or TABDLY are not 
	 * defined -- so print warning anyway
	 */
	SIGDPRINT(Debug,4,(&Debug,
			   "curses: No information is terminal driver expanding tabs!\n"));	  
#endif /* !defined(TABDLY) && !defined(OXTABS) */

#endif
	(void) ttsetattr(terminal_fd, &_raw_tty);
	if (use_tite && _start_termcap && do_tite) 
	    tputs(_start_termcap, 1, OUTCHAR);
	_intransmit = -1; /* state unclear */
	_inraw = 1;
	
	if (do_charset) {
	    switch_display_charset(1);
	    switch_title(1);
	}
	need_moveabsolute = 1;
    }
}

#ifdef TERMIOS

int toggle_lflag(T,enab,disab) 
     struct tf_state *T;
     int enab,disab;
{
    T->old_raw_ok = 0;

    if (! _inraw) {
	DPRINT(Debug,1,(&Debug,"toggle_lflag: _in_raw not set!\n"));
	return 0;
    }

    if (tcgetattr(terminal_fd,&_raw_tty) < 0) {
	DPRINT(Debug,1,(&Debug,"toggle_lflag: tcgetattr failed\n"));
	return 0;
    }
    T->old_raw = _raw_tty;
    T->old_raw_ok = 1;

    DPRINT(Debug,10,(&Debug,
		     "toggle_lflag: enable 0x%04x disable 0x%04x   old: 0x%04x\n",
		     enab,disab,_raw_tty.c_lflag));

    _raw_tty.c_lflag |= enab;
    _raw_tty.c_lflag &= ~disab;

    if (tcsetattr(terminal_fd,TCSANOW,&_raw_tty) < 0) {
	DPRINT(Debug,1,(&Debug,"reset_lfalg: tcsetattr failed\n"));
	return 0;
    }
    
    DPRINT(Debug,10,(&Debug,"toggle_lflag: set: 0x%04x\n",_raw_tty.c_lflag));

    return 1;
}

void reset_lfag(T)
     struct tf_state *T;
{
    if (!T->old_raw_ok) {
	DPRINT(Debug,1,(&Debug,"reset_lfalg: old_raw not ok\n"));
	return;
    }

    if (! _inraw) {
	DPRINT(Debug,1,(&Debug,"reset_lflag: _inraw not set!\n"));
	return;
    }

    if (tcsetattr(terminal_fd,TCSANOW,& (T->old_raw)) < 0) {
	DPRINT(Debug,1,(&Debug,"toggle_lflag: tcsetattr failed\n"));
	return;
    }

    DPRINT(Debug,10,(&Debug,"reset_lflag: set: 0x%04x\n",T->old_raw.c_lflag));
}
#endif

/* NOTE: Is called from signal handler! */
void transmit_functions(newstate)
     int newstate;
{
    /** turn function key transmission to ON | OFF **/
    
    if (newstate != _intransmit) {
	_intransmit = newstate;
	if (newstate == ON)
	    tputs(_transmit_on, 1, OUTCHAR);
	else 
	    tputs(_transmit_off, 1, OUTCHAR);
	FlushBuffer();      /* clear the output buffer */
    }
}


#ifdef ultrix
force_raw()
{
	(void) ttsetattr(terminal_fd,&_original_tty);
	_inraw = 0;
	Raw(ON);
}
#endif


int raw_off_called() {
    return need_moveabsolute;
}

#if !defined(TERMIO) && !defined(TERMIOS)
static int ttgetattr P_((int fd,
			 struct tty_modes *where));
static int ttgetattr(fd, where)
     int fd;
     struct tty_modes *where;
{
  if (ioctl(fd, TIOCGETP, &where->sgttyb) < 0)
    return(-1);
  if (ioctl(fd, TIOCGETC, &where->tchars) < 0)
    return(-1);
  return(0);
}

static int ttsetattr P_((int fd,
			 struct tty_modes *where));
static int ttsetattr(fd, where)
     int fd;
     struct tty_modes *where;
{
    if (ioctl(fd, TIOCSETP, &where->sgttyb) < 0)
	return(-1);
    if (ioctl(fd, TIOCSETC, &where->tchars) < 0)
	return(-1);
    return(0);
}
#endif

/* ----- following are copied from ../src/curses.c as documentation
         only ... they was not used on ELM ... also these termcap entries 
         seems not documented at least on Linux ....
*/ 


#if 0

static int _memory_locked = 0;		/* are we IN memlock??   */

int
HasMemlock()
{
    /** returns TRUE iff memory locking is available (a terminal
	feature that allows a specified portion of the screen to
	be "locked" & not cleared/scrolled... **/
    
    return ( _set_memlock && _clear_memlock );
}

static int _old_LINES;

int
StartMemlock()
{
    /** mark the current line as the "last" line of the portion to 
	be memory locked (always relative to the top line of the
	screen) Note that this will alter LINES so that it knows
	the top is locked.  This means that (plus) the program 
	will scroll nicely but (minus) End memlock MUST be called
	whenever we leave the locked-memory part of the program! **/
    
    if (! _set_memlock)
	return(-1);
    
    if (! _memory_locked) {
	
	_old_LINES = elm_LINES;
	elm_LINES -= _line;		/* we can't use this for scrolling */
	
	tputs(_set_memlock, 1, OUTCHAR);
	
	_memory_locked = TRUE;
    }

    return(0);
}

int
EndMemlock()
{
    /** Clear the locked memory condition...  **/
    
    if (! _set_memlock)
	return(-1);
    
    if (_memory_locked) {
	elm_LINES = _old_LINES;		/* back to old setting */
	
	tputs(_clear_memlock, 1, OUTCHAR);
       
	_memory_locked = FALSE;
    }
    return(0);
}

#endif /* ndef ELM */

/* ----------------------------------------------------------------------- */

#if 0  /* SCROLL */
static void set_scrolling_region P_((int line1, int line2));
static void set_scrolling_region(line1,line2) 
     int line1; 
     int line2;
{
      char *stuff, *tgoto();

      stuff = tgoto(_set_scroll_region,line1,line2);
      tputs(stuff, 1, OUTCHAR);
}

void limit_position(row,col)
     int *row; 
     int *col;
{
    if (*row < 0)
	*row = 0;
    if (*col < 0)
	*col = 0;
    if (*col >= _columns)
	*col = _columns - 1;

    if (*row >= _lines) {
	*row = _lines -1;
	*col = _columns - 1;
    }	   
}
#endif

/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 *  buffer-file-coding-system: iso-8859-1
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1