ftp.nice.ch/pub/next/unix/editor/ne-1.0.NI.s.tar.gz#/ne-1.0.NI.s/src/term.c

This is term.c in view mode; [Download] [Up]

/* Terminal control based on terminfo capabilities.

   Copyright (C) 1985, 1986, 1987 Free Software Foundation, Inc.
    Originally part of GNU Emacs.
    Vastly edited and modified for use within ne.

   Copyright (C) 1993 Sebastiano Vigna

    This file is part of ne, the nice editor.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2, or (at your option)
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */


#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#ifndef TERMCAP
#include <curses.h>
#include <term.h>
#else
#include "info2cap.h"
#endif

#include "termchar.h"
#include "cm.h"


/* When displaying errors about the terminal database, we try to print the
correct name. */

#ifdef TERMCAP
#define DATABASE_NAME "termcap"
#else
#define DATABASE_NAME "terminfo"
#endif


/* This is the real instantiation of the cm structure used by cm.c to hold
the cursor motion strings. */

struct cm Wcm;


/* These are the functions called from cm.c. */

void losecursor (void);
int Wcm_init (void);
void cmcostinit (void);
void cmgoto (int row, int col);



/* PORTABILITY PROBLEM: this macro is responsible for filtering nonprintable
characters. On systems with a wider system character set, it could be
redefined, for instance, in order to allow characters between 128 and 160 to
be printed. */

#define DECONTROL(c)		{ if ((unsigned char)(c) >= 127 && (unsigned char)(c) < 160) c = '?'; if ((unsigned char)(c) < ' ') (c) = '@'+(unsigned char)(c); }



#define OUTPUT(a) tputs (a, lines - curY, cmputc)
#define OUTPUT1(a) tputs (a, 1, cmputc)
#define OUTPUTL(a, lines) tputs (a, lines, cmputc)
#define OUTPUT_IF(a) { if (a) tputs (a, lines - curY, cmputc); }
#define OUTPUT1_IF(a) { if (a) tputs (a, 1, cmputc); }



/* Terminal charateristics that higher levels want to look at.
   These are all extern'd in termchar.h */

int	line_ins_del_ok;		/* Terminal can insert and delete lines */
int	char_ins_del_ok;		/* Terminal can insert and delete chars */
int	scroll_region_ok;		/* Terminal supports setting the scroll window */
int	standout_ok;			/* Terminal supports standout without magic cookies */
int	cursor_on_off_ok;		/* Terminal can make the cursor visible or invisible */


static int	RPov;		/* Least number of chars to start a TS_repeat.
								Less wouldn't be worth. */

static int	delete_in_insert_mode;	/* delete mode == insert mode */
static int	save_cursor_ok;			/* Terminal supports save and restore of cursor position */
static int	se_is_so;					/* 1 if same string both enters and leaves standout mode */

static int	standout_requested;	/* Nonzero when supposed to write text in standout mode. */
static int	insert_mode;			/* Nonzero when in insert mode. */
static int	standout_mode;			/* Nonzero when in standout mode. */


/* Size of window specified by higher levels. This is the number of lines,
starting from top of screen, to participate in ins/del line operations.
Effectively it excludes the bottom lines - specified_window_size lines from
those operations.  */

int	specified_window;



/* These functions ring a bell or flash the screen. If the service
is not available, the other one is tried. */


void ring_bell(void) {
	OUTPUT1_IF (bell ? bell : flash_screen);
}


void do_flash(void) {
	OUTPUT1_IF (flash_screen ? flash_screen : bell);
}



/* This function sets correctly the scroll region. Note that the cursor
is lost.  This function assumes scroll_region_ok == TRUE.  The cursor
position is lost, and from the terminfo specs. */

static void set_scroll_region (int start, int stop) {

	char *buf;

	assert(scroll_region_ok);

	if (save_cursor_ok) OUTPUT1(save_cursor);

	if (change_scroll_region) {
		buf = tparm (change_scroll_region, start, stop - 1);
	}
	else {
		buf = tparm (set_window, start, stop - 1, 0, columns - 1);
	}

	OUTPUT1(buf);

	if (save_cursor_ok) OUTPUT1(restore_cursor);
	else losecursor();
}


static void turn_on_insert (void) {
	if (!insert_mode)
		OUTPUT1(enter_insert_mode);
	insert_mode = TRUE;
}


static void turn_off_insert (void) {
	if (insert_mode)
		OUTPUT1(exit_insert_mode);
	insert_mode = FALSE;
}


/* These functions are called on all terminals in order to handle highlighting,
but do nothing on terminals with a magic cookie (or without standout).  */

static void turn_off_highlight (void) {
	if (standout_ok) {
		if (standout_mode)
			OUTPUT1(exit_standout_mode);
		standout_mode = FALSE;
	}
}


static void turn_on_highlight (void) {
	if (standout_ok) {
		if (!standout_mode)
			OUTPUT1(enter_standout_mode);
		standout_mode = TRUE;
	}
}


/* This function prepare the terminal for interactive I/O. It
initializes the terminal, prepares the cursor address mode, and
activates the keypad and the meta key. */

void set_terminal_modes(void) {

	/* Note that presently we do not support if and iprog, the program
	and the file which should be used, if present, to initialize the
	terminal. */

	OUTPUT1_IF(init_1string);
	OUTPUT1_IF(init_2string);
	OUTPUT1_IF(init_3string);

	OUTPUT1_IF(enter_ca_mode);
	OUTPUT1_IF(keypad_xmit);

	if (has_meta_key) OUTPUT1_IF(meta_on);

	losecursor();
}


/* This function puts again the terminal in its normal state. */

void reset_terminal_modes (void) {

	turn_off_highlight ();
	turn_off_insert ();

	OUTPUT1_IF (keypad_local);
	OUTPUT1_IF (exit_ca_mode);
}


/* This function sets the variable specified_window. Following
line insert/delete operations will be limited to lines 0 to (size-1). */

void set_terminal_window(int size) {

	specified_window = size ? size : lines;

}




/* These functions are the external interface to standout activation. */

void standout_on (void) {
	standout_requested = TRUE;
}


void standout_off (void) {
	standout_requested = FALSE;
}


/* These functions are the external interface to cursor on/off strings. */

void cursor_on (void) {
	if (cursor_on_off_ok) OUTPUT1(cursor_normal);
}


void cursor_off (void) {
	if (cursor_on_off_ok) OUTPUT1(cursor_invisible);
}


/* Set standout mode to the mode specified for the text to be output.  */

static void highlight_if_desired (void) {

	if (standout_requested) turn_on_highlight ();
	else turn_off_highlight ();
}


/* Move to absolute position, specified origin 0 */

void move_cursor (int row, int col) {

	if (curY == row && curX == col) return;

	if (!move_standout_mode) turn_off_highlight ();

	if (!move_insert_mode) turn_off_insert ();

	cmgoto (row, col);
}


/* This function clears from the cursor position to the end of line. It assumes
that the line is already clear starting at column first_unused_hpos. Note that
the cursor may be moved, on terminals lacking a `ce' string.  */

void clear_end_of_line(int first_unused_hpos) {

	int i;

	if (curX >= first_unused_hpos) return;

	if (clr_eol) {
		OUTPUT1 (clr_eol);
	}
	else {

		/* We have to do it the hard way. */

		turn_off_insert ();

		for (i = curX; i < first_unused_hpos; i++) putchar (' ');

		cmplus (first_unused_hpos - curX);
	}
}


/* Shorthand; use this if you don't know anything about the state
of the line. */

void clear_to_eol(void) {
	clear_end_of_line(columns);
}


/* This function clears from the cursor position to the end of screen */

void clear_to_end (void) {

	int i;

	if (clr_eos) {
		OUTPUT(clr_eos);
	}
	else {
		for (i = curY; i < lines; i++) {
			move_cursor (i, 0);
			clear_to_eol();
		}
	}
}


/* This function clears the entire screen */

void clear_entire_screen (void) {

	if (clear_screen) {
		OUTPUTL(clear_screen, lines);
		cmat (0, 0);
	}
	else {
		move_cursor (0, 0);
		clear_to_end();
	}
}


/* This function outputs len characters pointed at by string. The characters
will be truncated to the end of the current line. Passing a NULL for string
results in outputting spaces. A len of 0 causes no action. */

void output_chars (const char *string, int len) {

	const char *p;
	char *buf;
	int	c;
	const char *first_check;

	if (len == 0) return;

	highlight_if_desired ();
	turn_off_insert ();

	if (curX + len > columns) len = columns - curX;

	/* Don't dare write in last column of bottom line, if AutoWrap,
	since that would scroll the whole screen on some terminals.  */

	if (AutoWrap && curY + 1 == lines && curX + len == columns) len--;

	cmplus (len);

	if (string == NULL) {
		if (len > RPov) {
			buf = tparm (repeat_char, ' ', len);
			OUTPUT1(buf);
		}
		else {
			while(--len >= 0) putchar(' ');
		}
		return;
	}

	first_check = string;

	if (RPov > len && !transparent_underline && !tilde_glitch) {
		while(--len >= 0) {
			c = *string++;
			DECONTROL(c);
			putchar(c);
		}
	} else
		while (--len >= 0) {
			c = *string;
			if (RPov + 1 < len && string >= first_check) {
				int	repeat_count;

				p = string;

				/* Now, len is number of chars left starting at p */
				while (++p - string <= len && *p == c);

				repeat_count = p - string;

				if (repeat_count > RPov) {
					buf = tparm (repeat_char, c, repeat_count);
					OUTPUT1(buf);
					string = p;
					len -= repeat_count - 1;
					continue;
				}
				else {
					/* If all N identical chars are too few,
		 			don't even consider the last N-1, the last N-2,...  */

					first_check = p;
				}
			}

			if (c == '_' && transparent_underline) {
				putchar (' ');
				OUTPUT1(Left);
			}

			if (tilde_glitch && c == '~')
				c = '`';

			DECONTROL(c);
			putchar (c);
			string++;
		}
}



/* Same as output_chars(), but inserts instead. */

void insert_chars (const char *start, int len) {

	char *buf;
	int c;

	if (len == 0) return;

	highlight_if_desired ();

	if (parm_ich) {

		buf = tparm (parm_ich, len);
		OUTPUT1 (buf);

		if (start) output_chars (start, len);

		return;
	}

	if (curX + len > columns) len = columns - curX;

	/* Don't dare write in last column of bottom line, if AutoWrap,
	since that would scroll the whole screen on some terminals.  */

	if (AutoWrap && curY + 1 == lines && curX + len == columns) len--;

	turn_on_insert ();
	cmplus (len);

	if (!transparent_underline && !tilde_glitch && start
	     && insert_padding == NULL && insert_character == NULL) {
		while(--len >= 0) {
			c = *start++;
			DECONTROL(c);
			putchar(c);
		}
	}
	else
		while (--len >= 0) {

			OUTPUT1_IF (insert_character);

			if (!start) c = ' ';
			else {
				c = *start++;
				if (tilde_glitch && c == '~') c = '`';
			}

			DECONTROL(c);
			putchar (c);
			OUTPUT1_IF(insert_padding);
		}
}



/* This function deletes n characters at the current cursor position. */

void delete_chars (int n) {

	char *buf;

	if (delete_in_insert_mode) {
		turn_on_insert();
	}
	else {
		turn_off_insert();
		OUTPUT1_IF(enter_delete_mode);
	}

	if (parm_dch) {
		buf = tparm(parm_dch, n);
		OUTPUT1(buf);
	}
	else
		while(--n>=0) OUTPUT1(delete_character);

	if (!delete_in_insert_mode)
		OUTPUT_IF(exit_delete_mode);
}


/* This internal function will do an insertion or deletion
for n lines, given a parametrized and/or a one-line capability
for that purpose. */

static void do_multi_ins_del(char *multi, char *single, int n) {

	if (multi) {

		char *buf;

		buf = tparm(multi, n);
		OUTPUT(buf);
	}
	else {
		while(--n>=0) OUTPUT(single);
	}
}


/* This function inserts n lines at vertical position vpos. If n is negative,
it deletes -n lines. specified_window is taken into account. This function
assumes line_ins_del_ok == TRUE. */

void ins_del_lines (int vpos, int n) {

	int i = n > 0 ? n : -n;

	assert(line_ins_del_ok);
	assert(i != 0);
	assert(vpos < specified_window);

	if (scroll_region_ok && vpos + i >= specified_window)
		return;

	if (!memory_below && vpos + i >= lines)
		return;

	if (scroll_region_ok) {
		if (specified_window != lines)
			set_scroll_region(vpos, specified_window);

		if (n < 0) {
			move_cursor(specified_window - 1, 0);
			while (--i >= 0)
				OUTPUTL(scroll_forward, specified_window-vpos+1);
		}
		else {
			move_cursor(vpos, 0);
			while (--i >= 0)
				OUTPUTL(scroll_reverse, specified_window-vpos+1);
		}

		if (specified_window != lines)
			set_scroll_region(0, lines);
	}
	else {
		if (n > 0) {
			if (specified_window != lines) {
				move_cursor(specified_window - i, 0);
				do_multi_ins_del(parm_delete_line, delete_line, i);
			}

			move_cursor(vpos, 0);
			do_multi_ins_del(parm_insert_line, insert_line, i);
		}
		else {
			move_cursor(vpos, 0);
			do_multi_ins_del(parm_delete_line, delete_line, i);

			if (specified_window != lines) {
				move_cursor(specified_window-i, 0);
				do_multi_ins_del(parm_insert_line, insert_line, i);
			}
			else if (memory_below) {
				move_cursor(lines + n, 0);
				clear_to_end ();
			}
		}
	}
}


extern int cost;		/* In cm.c */
extern int evalcost(int);


/* This function performs the cursor motion cost setup, and sets the
variable RPov to the number of characters (with padding) which are
really output when repeating one character. */

static void calculate_costs (void) {

	if (repeat_char) {

		char *buf = tparm(repeat_char, ' ', 1);

		cost = 0;
		tputs(buf, 1, evalcost);

		RPov = cost+1;
	}
	else RPov = columns * 2;

	cmcostinit();
}


/* This is the main terminal initialization function. It sets up Wcm,
patches here and there the terminfo database, calculates the costs, and
initializes the terminal characteristics variables. Note that this function
can exit(). */

void term_init (void) {

	/* First of all we initialize the terminfo database. */

	setupterm(0,1,0);

	ColPosition = column_address;
	RowPosition = row_address;
	AbsPosition = cursor_address;
	CR = carriage_return;
	Home = cursor_home;
	LastLine = cursor_to_ll;
	Right = cursor_right;
	Down = cursor_down;
	Left = cursor_left;
	Up = cursor_up;
	AutoWrap = auto_right_margin;
	MagicWrap = eat_newline_glitch;
	ScreenRows = lines;
	ScreenCols = columns;

	if (!bell) bell = "\07";

	if (!scroll_forward) scroll_forward = Down;
	if (!scroll_reverse) scroll_reverse = Up;

	if (key_backspace && key_left && !strcmp(key_backspace, key_left)) {
		/* In case left and backspace produce the same sequence,
		we want to get key_left. */

		key_backspace = NULL;
	}

	specified_window = lines;

	if (Wcm_init ())	{

		/* We can't do cursor motion */

		if (generic_type) {
			printf("Your terminal type is a generic terminal, not a real\nterminal, and it lacks the ability to position the cursor.\nPlease check that the variable TERM is set correctly, and that\nyour " DATABASE_NAME "database is up to date.\n");
		}
		else {
			printf("Your terminal type is not powerful enough to run ne:\nit lacks the ability to position the cursor.\nPlease check that the variable TERM is set correctly, and that\nyour " DATABASE_NAME "database is up to date.\n");
		}

		resetterm();
		exit(1);
	}

	calculate_costs();

	delete_in_insert_mode
	     = enter_delete_mode && enter_insert_mode
	     && !strcmp (enter_delete_mode, enter_insert_mode);

	se_is_so = enter_standout_mode && exit_standout_mode
	     && !strcmp (enter_standout_mode, exit_standout_mode);

	scroll_region_ok = set_window || change_scroll_region;

	line_ins_del_ok = (((insert_line || parm_insert_line)
	     && (delete_line || parm_delete_line))
	     || (scroll_region_ok
	     && scroll_forward
	     && scroll_reverse));

	char_ins_del_ok = ((insert_character || enter_insert_mode ||
	    insert_padding || parm_ich)
	     && (delete_character || parm_dch));

	save_cursor_ok = (save_cursor && restore_cursor);

	standout_ok = (enter_standout_mode && exit_standout_mode && magic_cookie_glitch < 0);

	cursor_on_off_ok = (cursor_invisible && cursor_normal);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.