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

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

/* Terminfo database scanning and keyboard escape sequence matching functions.

   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 "ne.h"
#include <time.h>
#include <signal.h>
#include <errno.h>
#include <string.h>

#ifdef _AMIGA
#include <proto/dos.h>
#endif


/* Maximum number of key definitions from terminfo */

#define MAX_TERM_KEY 100

/* Size of the keyboard input buffer. */

#define KBD_BUF_SIZE 512



/* This structure describes a key in the terminfo database. These structures
are ordered with respect to the string field, in order to greatly optimize
their scanning. */


typedef struct {
	unsigned char *string;
	int code;
} term_key;



static term_key key[MAX_TERM_KEY];
static int num_keys = 0;

/* Function to pass to qsort for sorting the key capabilities array. */

static int keycmp(const void *t1, const void *t2) {

	return(-strcmp((char *)((term_key *)t1)->string, (char *)((term_key *)t2)->string));

}




/* This function sets the first free position in the key capabilities array
to the cap_string capability, and increment the first free position counter.
*/

static void key_set(char *cap_string, int code) {

	if (!cap_string) return;

	key[num_keys].string = (unsigned char *)cap_string;
	key[num_keys].code = code;
	num_keys++;

}

/* Here we scan the terminfo database and build a term_key structure for
each key available. num_keys records the number of entries. The array is
sorted in reverse order with respect to string field (this optimizes the
comparisons, assuming that usually almost all control sequences start with a
character smaller than ' ', while the characters typed by the user are
almost always greater than or equal to ' '). */


void read_key_capabilities(void) {


	/* Cursor movement keys */

	key_set(key_up, NE_KEY_UP);
	key_set(key_down, NE_KEY_DOWN);
	key_set(key_left, NE_KEY_LEFT);
	key_set(key_right, NE_KEY_RIGHT);
	key_set(key_home, NE_KEY_HOME);
	key_set(key_ll, NE_KEY_HOME_DOWN);
	key_set(key_npage, NE_KEY_NPAGE);
	key_set(key_ppage, NE_KEY_PPAGE);
	key_set(key_sf, NE_KEY_SCROLL_FORWARD);
	key_set(key_sr, NE_KEY_SCROLL_REVERSE);


	/* Editing keys */

	key_set(key_eol, NE_KEY_CLEAR_TO_EOL);
	key_set(key_eos, NE_KEY_CLEAR_TO_EOS);
	key_set(key_backspace, NE_KEY_BACKSPACE);
	key_set(key_dl, NE_KEY_DELETE_LINE);
	key_set(key_il, NE_KEY_INSERT_LINE);
	key_set(key_dc, NE_KEY_DELETE_CHAR);
	key_set(key_ic, NE_KEY_INSERT_CHAR);
	key_set(key_eic, NE_KEY_EXIT_INSERT_CHAR);
	key_set(key_clear, NE_KEY_CLEAR);


	/* Keypad keys */

	key_set(key_a1, NE_KEY_A1);
	key_set(key_a3, NE_KEY_A3);
	key_set(key_b2, NE_KEY_B2);
	key_set(key_c1, NE_KEY_C1);
	key_set(key_c3, NE_KEY_C3);


	/* Tab keys (never used in the standard configuration) */

	key_set(key_catab, NE_KEY_CLEAR_ALL_TABS);
	key_set(key_ctab, NE_KEY_CLEAR_TAB);
	key_set(key_stab, NE_KEY_SET_TAB);


	/* Function keys */

	key_set(key_f0, NE_KEY_F(0));
	key_set(key_f1, NE_KEY_F(1));
	key_set(key_f2, NE_KEY_F(2));
	key_set(key_f3, NE_KEY_F(3));
	key_set(key_f4, NE_KEY_F(4));
	key_set(key_f5, NE_KEY_F(5));
	key_set(key_f6, NE_KEY_F(6));
	key_set(key_f7, NE_KEY_F(7));
	key_set(key_f8, NE_KEY_F(8));
	key_set(key_f9, NE_KEY_F(9));
	key_set(key_f10, NE_KEY_F(10));
	key_set(key_f11, NE_KEY_F(11));
	key_set(key_f12, NE_KEY_F(12));
	key_set(key_f13, NE_KEY_F(13));
	key_set(key_f14, NE_KEY_F(14));
	key_set(key_f15, NE_KEY_F(15));
	key_set(key_f16, NE_KEY_F(16));
	key_set(key_f17, NE_KEY_F(17));
	key_set(key_f18, NE_KEY_F(18));
	key_set(key_f19, NE_KEY_F(19));
	key_set(key_f20, NE_KEY_F(20));
	key_set(key_f21, NE_KEY_F(21));
	key_set(key_f22, NE_KEY_F(22));
	key_set(key_f23, NE_KEY_F(23));
	key_set(key_f24, NE_KEY_F(24));
	key_set(key_f25, NE_KEY_F(25));
	key_set(key_f26, NE_KEY_F(26));
	key_set(key_f27, NE_KEY_F(27));
	key_set(key_f28, NE_KEY_F(28));
	key_set(key_f29, NE_KEY_F(29));
	key_set(key_f30, NE_KEY_F(30));
	key_set(key_f31, NE_KEY_F(31));
	key_set(key_f32, NE_KEY_F(32));
	key_set(key_f33, NE_KEY_F(33));
	key_set(key_f34, NE_KEY_F(34));
	key_set(key_f35, NE_KEY_F(35));
	key_set(key_f36, NE_KEY_F(36));
	key_set(key_f37, NE_KEY_F(37));
	key_set(key_f38, NE_KEY_F(38));
	key_set(key_f39, NE_KEY_F(39));
	key_set(key_f40, NE_KEY_F(40));
	key_set(key_f41, NE_KEY_F(41));
	key_set(key_f42, NE_KEY_F(42));
	key_set(key_f43, NE_KEY_F(43));
	key_set(key_f44, NE_KEY_F(44));
	key_set(key_f45, NE_KEY_F(45));
	key_set(key_f46, NE_KEY_F(46));
	key_set(key_f47, NE_KEY_F(47));
	key_set(key_f48, NE_KEY_F(48));
	key_set(key_f49, NE_KEY_F(49));
	key_set(key_f50, NE_KEY_F(50));
	key_set(key_f51, NE_KEY_F(51));
	key_set(key_f52, NE_KEY_F(52));
	key_set(key_f53, NE_KEY_F(53));
	key_set(key_f54, NE_KEY_F(54));
	key_set(key_f55, NE_KEY_F(55));
	key_set(key_f56, NE_KEY_F(56));
	key_set(key_f57, NE_KEY_F(57));
	key_set(key_f58, NE_KEY_F(58));
	key_set(key_f59, NE_KEY_F(59));
	key_set(key_f60, NE_KEY_F(60));
	key_set(key_f61, NE_KEY_F(61));
	key_set(key_f62, NE_KEY_F(62));
	key_set(key_f63, NE_KEY_F(63));

	/* Fake (simulated) command key. */

	key_set("\x1B:", NE_KEY_COMMAND);

	assert(num_keys<MAX_TERM_KEY-1);

	D(printf("Got %d keys from terminfo\n\r", num_keys);)

	qsort(key, num_keys, sizeof(term_key), keycmp);

}




/* This function sets the escape time, which is an option, but it's global to
ne and it's not saved in autopreferences files. However, an EscapeTime command
can be attached manually to any preferences file. */

static escape_time = 10;

void set_escape_time(int new_escape_time) {
	escape_time = new_escape_time;
}


/* This function sets the current timeout in the termios structure relative
to stdin. If the timeout value (in tenth of a second) is positive, VMIN
is set to 0, otherwise to 1. */

#ifndef _AMIGA

static void set_termios_timeout(int timeout) {

	struct termios termios;

	tcgetattr(0, &termios);

	termios.c_cc[VTIME] = timeout;
	termios.c_cc[VMIN] = timeout ? 0 : 1;

	tcsetattr(0, TCSANOW, &termios);
}

#endif


/* This function reads in characters, and tries to match them with the
sequences corresponding to special keys. It tries to be highly optimized and
efficient by employing a sorted array of strings for the terminal keys. An
index keeps track of the key which has a partial match with the current
contents of the keyboard buffer. As each character is input, a match is
tried with the rest of the string. If a new character does not match, we can
just increment the key counter (because the array is sorted). When we get
out of the array, we give back the first char in the keyboard buffer (the
next call will retry a match on the following chars). */


int get_key_code(void) {

	static int cur_len = 0;
	static unsigned char kbd_buffer[KBD_BUF_SIZE];

	int c, last_match = 0, cur_key = 0, partial_match = FALSE;

	while(TRUE) {

		if (cur_len) {

			/* Something is already in the buffer. last_match is the position
			we have to check. */

			while(last_match < cur_len) {

				/* First easy case. We felt off the array. We return the first character
				in the buffer and restart the match. */

				if (!key[cur_key].string) {
					c = kbd_buffer[0];
					if (--cur_len) memmove(kbd_buffer, kbd_buffer+1, cur_len);
					return(c);
				}

				/* Second case. We have a partial match on the first last_match
				characters. If another character matches, either the string is terminated,
				and we return the key code, or we increment the match count. */


				else if (key[cur_key].string[last_match] == kbd_buffer[last_match]) {
					if (key[cur_key].string[last_match+1] == 0) {
						if (cur_len -= last_match+1) memmove(kbd_buffer, kbd_buffer+last_match+1, cur_len);

						assert(key[cur_key].code < NUM_KEYS);

						return(key[cur_key].code);
					}
					else last_match++;
				}

				/* The tricky part. If there is a failed match, the order guarantees that
				no match if possible if the code of the keyboard char is greater than the code of
				the capability char. Otherwise, we check for the first capability starting
				with the current keyboard characters. */

				else {
					if (kbd_buffer[last_match] > key[cur_key].string[last_match]) {
						c = kbd_buffer[0];
						if (--cur_len) memmove(kbd_buffer, kbd_buffer+1, cur_len);
						return(c);
					}
					else {
						last_match = 0;
						cur_key++;
					}
				}
			}

			/* If we have a partial match, let's look at stdin for escape_time
			tenths of second. If nothing arrives, it is probably time to return
			what we got. Note that this won't work properly if the terminal has
			a key capability which is a prefix of another key capability. */

			partial_match = TRUE;
		}

		fflush(stdout);

#ifdef _AMIGA
		if (!partial_match || WaitForChar(Input(), 100000*escape_time)) c = getchar();
		else c = EOF;
#else
		if (partial_match) set_termios_timeout(escape_time);

		errno = 0;

		c = getchar();

		if (c == EOF && (!partial_match || errno) && errno != EINTR) kill(getpid(), SIGTERM);

		if (partial_match) set_termios_timeout(0);
#endif

		partial_match = FALSE;

		if (c != EOF) {
			if (cur_len < KBD_BUF_SIZE) kbd_buffer[cur_len++] = c;
		}
		else {
			if (cur_len) {

				/* We ran out of time. The current partial match is discarded,
				and the first character of the keyboard buffer is returned. */

				c = kbd_buffer[0];
				if (--cur_len) memmove(kbd_buffer, kbd_buffer+1, cur_len);
				return(c);
			}
		}
	}
}

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