1
0
mirror of https://github.com/rfivet/uemacs.git synced 2024-12-18 15:26:23 -05:00
uemacs/input.c
Linus Torvalds c2a7e41fae Turn ESC+'[' into a CSI character
This avoids the annoying behavior where we're on the command line,
waiting for an ESC, and any control character sequence ends up finishing
the command line and eating the first ESC.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-06-07 11:52:42 -07:00

720 lines
14 KiB
C

/* INPUT.C
*
* Various input routines
*
* written by Daniel Lawrence 5/9/86
* modified by Petri Kutvonen
*/
#include <stdio.h>
#include <unistd.h>
#include "estruct.h"
#include "edef.h"
#include "efunc.h"
#if PKCODE
#if MSDOS && TURBO
#include <dir.h>
#endif
#endif
#if PKCODE && (UNIX || (MSDOS && TURBO))
#define COMPLC 1
#else
#define COMPLC 0
#endif
/*
* Ask a yes or no question in the message line. Return either TRUE, FALSE, or
* ABORT. The ABORT status is returned if the user bumps out of the question
* with a ^G. Used any time a confirmation is required.
*/
int mlyesno(char *prompt)
{
char c; /* input character */
char buf[NPAT]; /* prompt to user */
for (;;) {
/* build and prompt the user */
strcpy(buf, prompt);
strcat(buf, " (y/n)? ");
mlwrite(buf);
/* get the responce */
c = tgetc();
if (c == ectoc(abortc)) /* Bail out! */
return (ABORT);
if (c == 'y' || c == 'Y')
return (TRUE);
if (c == 'n' || c == 'N')
return (FALSE);
}
}
/*
* Write a prompt into the message line, then read back a response. Keep
* track of the physical position of the cursor. If we are in a keyboard
* macro throw the prompt away, and return the remembered response. This
* lets macros run at full speed. The reply is always terminated by a carriage
* return. Handle erase, kill, and abort keys.
*/
int mlreply(char *prompt, char *buf, int nbuf)
{
return (nextarg(prompt, buf, nbuf, ctoec('\n')));
}
int mlreplyt(char *prompt, char *buf, int nbuf, int eolchar)
{
return (nextarg(prompt, buf, nbuf, eolchar));
}
/*
* ectoc:
* expanded character to character
* collapse the CONTROL and SPEC flags back into an ascii code
*/
int ectoc(int c)
{
if (c & CONTROL)
c = c & ~(CONTROL | 0x40);
if (c & SPEC)
c = c & 255;
return (c);
}
/*
* ctoec:
* character to extended character
* pull out the CONTROL and SPEC prefixes (if possible)
*/
int ctoec(int c)
{
if (c >= 0x00 && c <= 0x1F)
c = CONTROL | (c + '@');
return (c);
}
/*
* get a command name from the command line. Command completion means
* that pressing a <SPACE> will attempt to complete an unfinished command
* name if it is unique.
*/
fn_t getname(void)
{
register int cpos; /* current column on screen output */
register int c;
register char *sp; /* pointer to string for output */
register NBIND *ffp; /* first ptr to entry in name binding table */
register NBIND *cffp; /* current ptr to entry in name binding table */
register NBIND *lffp; /* last ptr to entry in name binding table */
char buf[NSTRING]; /* buffer to hold tentative command name */
/* starting at the beginning of the string buffer */
cpos = 0;
/* if we are executing a command line get the next arg and match it */
if (clexec) {
if (macarg(buf) != TRUE)
return NULL;
return (fncmatch(&buf[0]));
}
/* build a name string from the keyboard */
while (TRUE) {
c = tgetc();
/* if we are at the end, just match it */
if (c == 0x0d) {
buf[cpos] = 0;
/* and match it off */
return (fncmatch(&buf[0]));
} else if (c == ectoc(abortc)) { /* Bell, abort */
ctrlg(FALSE, 0);
TTflush();
return NULL;
} else if (c == 0x7F || c == 0x08) { /* rubout/erase */
if (cpos != 0) {
TTputc('\b');
TTputc(' ');
TTputc('\b');
--ttcol;
--cpos;
TTflush();
}
} else if (c == 0x15) { /* C-U, kill */
while (cpos != 0) {
TTputc('\b');
TTputc(' ');
TTputc('\b');
--cpos;
--ttcol;
}
TTflush();
} else if (c == ' ' || c == 0x1b || c == 0x09) {
/* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
/* attempt a completion */
buf[cpos] = 0; /* terminate it for us */
ffp = &names[0]; /* scan for matches */
while (ffp->n_func != NULL) {
if (strncmp(buf, ffp->n_name, strlen(buf))
== 0) {
/* a possible match! More than one? */
if ((ffp + 1)->n_func == NULL ||
(strncmp
(buf, (ffp + 1)->n_name,
strlen(buf)) != 0)) {
/* no...we match, print it */
sp = ffp->n_name + cpos;
while (*sp)
TTputc(*sp++);
TTflush();
return (ffp->n_func);
} else {
/* << << << << << << << << << << << << << << << << << */
/* try for a partial match against the list */
/* first scan down until we no longer match the current input */
lffp = (ffp + 1);
while ((lffp +
1)->n_func !=
NULL) {
if (strncmp
(buf,
(lffp +
1)->n_name,
strlen(buf))
!= 0)
break;
++lffp;
}
/* and now, attempt to partial complete the string, char at a time */
while (TRUE) {
/* add the next char in */
buf[cpos] =
ffp->
n_name[cpos];
/* scan through the candidates */
cffp = ffp + 1;
while (cffp <=
lffp) {
if (cffp->
n_name
[cpos]
!=
buf
[cpos])
goto onward;
++cffp;
}
/* add the character */
TTputc(buf
[cpos++]);
}
/* << << << << << << << << << << << << << << << << << */
}
}
++ffp;
}
/* no match.....beep and onward */
TTbeep();
onward:;
TTflush();
/* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
} else {
if (cpos < NSTRING - 1 && c > ' ') {
buf[cpos++] = c;
TTputc(c);
}
++ttcol;
TTflush();
}
}
}
/* tgetc: Get a key from the terminal driver, resolve any keyboard
macro action */
int tgetc(void)
{
int c; /* fetched character */
/* if we are playing a keyboard macro back, */
if (kbdmode == PLAY) {
/* if there is some left... */
if (kbdptr < kbdend)
return ((int) *kbdptr++);
/* at the end of last repitition? */
if (--kbdrep < 1) {
kbdmode = STOP;
#if VISMAC == 0
/* force a screen update after all is done */
update(FALSE);
#endif
} else {
/* reset the macro to the begining for the next rep */
kbdptr = &kbdm[0];
return ((int) *kbdptr++);
}
}
/* fetch a character from the terminal driver */
c = TTgetc();
/* record it for $lastkey */
lastkey = c;
/* save it if we need to */
if (kbdmode == RECORD) {
*kbdptr++ = c;
kbdend = kbdptr;
/* don't overrun the buffer */
if (kbdptr == &kbdm[NKBDM - 1]) {
kbdmode = STOP;
TTbeep();
}
}
/* and finally give the char back */
return (c);
}
/* GET1KEY: Get one keystroke. The only prefixs legal here
are the SPEC and CONTROL prefixes.
*/
int get1key(void)
{
int c;
/* get a keystroke */
c = tgetc();
#if MSDOS
if (c == 0) { /* Apply SPEC prefix */
c = tgetc();
if (c >= 0x00 && c <= 0x1F) /* control key? */
c = CONTROL | (c + '@');
return (SPEC | c);
}
#endif
if (c >= 0x00 && c <= 0x1F) /* C0 control -> C- */
c = CONTROL | (c + '@');
return (c);
}
/* GETCMD: Get a command from the keyboard. Process all applicable
prefix keys
*/
int getcmd(void)
{
int c; /* fetched keystroke */
#if VT220
int d; /* second character P.K. */
int cmask = 0;
#endif
/* get initial character */
c = get1key();
#if VT220
proc_metac:
#endif
if (c == 128+27) /* CSI */
goto handle_CSI;
/* process META prefix */
if (c == (CONTROL | '[')) {
c = get1key();
#if VT220
if (c == '[' || c == 'O') { /* CSI P.K. */
handle_CSI:
c = get1key();
if (c >= 'A' && c <= 'D')
return (SPEC | c | cmask);
if (c >= 'E' && c <= 'z' && c != 'i' && c != 'c')
return (SPEC | c | cmask);
d = get1key();
if (d == '~') /* ESC [ n ~ P.K. */
return (SPEC | c | cmask);
switch (c) { /* ESC [ n n ~ P.K. */
case '1':
c = d + 32;
break;
case '2':
c = d + 48;
break;
case '3':
c = d + 64;
break;
default:
c = '?';
break;
}
if (d != '~') /* eat tilde P.K. */
get1key();
if (c == 'i') { /* DO key P.K. */
c = ctlxc;
goto proc_ctlxc;
} else if (c == 'c') /* ESC key P.K. */
c = get1key();
else
return (SPEC | c | cmask);
}
#endif
#if VT220
if (c == (CONTROL | '[')) {
cmask = META;
goto proc_metac;
}
#endif
if (islower(c)) /* Force to upper */
c ^= DIFCASE;
if (c >= 0x00 && c <= 0x1F) /* control key */
c = CONTROL | (c + '@');
return (META | c);
}
#if PKCODE
else if (c == metac) {
c = get1key();
#if VT220
if (c == (CONTROL | '[')) {
cmask = META;
goto proc_metac;
}
#endif
if (islower(c)) /* Force to upper */
c ^= DIFCASE;
if (c >= 0x00 && c <= 0x1F) /* control key */
c = CONTROL | (c + '@');
return (META | c);
}
#endif
#if VT220
proc_ctlxc:
#endif
/* process CTLX prefix */
if (c == ctlxc) {
c = get1key();
#if VT220
if (c == (CONTROL | '[')) {
cmask = CTLX;
goto proc_metac;
}
#endif
if (c >= 'a' && c <= 'z') /* Force to upper */
c -= 0x20;
if (c >= 0x00 && c <= 0x1F) /* control key */
c = CONTROL | (c + '@');
return (CTLX | c);
}
/* otherwise, just return it */
return (c);
}
/* A more generalized prompt/reply function allowing the caller
to specify the proper terminator. If the terminator is not
a return ('\n') it will echo as "<NL>"
*/
int getstring(char *prompt, char *buf, int nbuf, int eolchar)
{
register int cpos; /* current character position in string */
register int c;
register int quotef; /* are we quoting the next char? */
#if COMPLC
int ffile, ocpos, nskip = 0, didtry = 0;
#if MSDOS
struct ffblk ffblk;
char *fcp;
#endif
#if UNIX
static char tmp[] = "/tmp/meXXXXXX";
FILE *tmpf = NULL;
#endif
ffile = (strcmp(prompt, "Find file: ") == 0
|| strcmp(prompt, "View file: ") == 0
|| strcmp(prompt, "Insert file: ") == 0
|| strcmp(prompt, "Write file: ") == 0
|| strcmp(prompt, "Read file: ") == 0
|| strcmp(prompt, "File to execute: ") == 0);
#endif
cpos = 0;
quotef = FALSE;
/* prompt the user for the input string */
mlwrite(prompt);
for (;;) {
#if COMPLC
if (!didtry)
nskip = -1;
didtry = 0;
#endif
/* get a character from the user */
c = get1key();
/* If it is a <ret>, change it to a <NL> */
#if PKCODE
if (c == (CONTROL | 0x4d) && !quotef)
#else
if (c == (CONTROL | 0x4d))
#endif
c = CONTROL | 0x40 | '\n';
/* if they hit the line terminate, wrap it up */
if (c == eolchar && quotef == FALSE) {
buf[cpos++] = 0;
/* clear the message line */
mlwrite("");
TTflush();
/* if we default the buffer, return FALSE */
if (buf[0] == 0)
return (FALSE);
return (TRUE);
}
/* change from command form back to character form */
c = ectoc(c);
if (c == ectoc(abortc) && quotef == FALSE) {
/* Abort the input? */
ctrlg(FALSE, 0);
TTflush();
return (ABORT);
} else if ((c == 0x7F || c == 0x08) && quotef == FALSE) {
/* rubout/erase */
if (cpos != 0) {
outstring("\b \b");
--ttcol;
if (buf[--cpos] < 0x20) {
outstring("\b \b");
--ttcol;
}
if (buf[cpos] == '\n') {
outstring("\b\b \b\b");
ttcol -= 2;
}
TTflush();
}
} else if (c == 0x15 && quotef == FALSE) {
/* C-U, kill */
while (cpos != 0) {
outstring("\b \b");
--ttcol;
if (buf[--cpos] < 0x20) {
outstring("\b \b");
--ttcol;
}
if (buf[cpos] == '\n') {
outstring("\b\b \b\b");
ttcol -= 2;
}
}
TTflush();
#if COMPLC
} else if ((c == 0x09 || c == ' ') && quotef == FALSE
&& ffile) {
/* TAB, complete file name */
char ffbuf[255];
#if MSDOS
char sffbuf[128];
int lsav = -1;
#endif
int n, iswild = 0;
didtry = 1;
ocpos = cpos;
while (cpos != 0) {
outstring("\b \b");
--ttcol;
if (buf[--cpos] < 0x20) {
outstring("\b \b");
--ttcol;
}
if (buf[cpos] == '\n') {
outstring("\b\b \b\b");
ttcol -= 2;
}
if (buf[cpos] == '*' || buf[cpos] == '?')
iswild = 1;
#if MSDOS
if (lsav < 0 && (buf[cpos] == '\\' ||
buf[cpos] == '/' ||
buf[cpos] == ':'
&& cpos == 1))
lsav = cpos;
#endif
}
TTflush();
if (nskip < 0) {
buf[ocpos] = 0;
#if UNIX
if (tmpf != NULL)
fclose(tmpf);
strcpy(tmp, "/tmp/meXXXXXX");
strcpy(ffbuf, "echo ");
strcat(ffbuf, buf);
if (!iswild)
strcat(ffbuf, "*");
strcat(ffbuf, " >");
mkstemp(tmp);
strcat(ffbuf, tmp);
strcat(ffbuf, " 2>&1");
system(ffbuf);
tmpf = fopen(tmp, "r");
#endif
#if MSDOS
strcpy(sffbuf, buf);
if (!iswild)
strcat(sffbuf, "*.*");
#endif
nskip = 0;
}
#if UNIX
c = ' ';
for (n = nskip; n > 0; n--)
while ((c = getc(tmpf)) != EOF
&& c != ' ');
#endif
#if MSDOS
if (nskip == 0) {
strcpy(ffbuf, sffbuf);
c = findfirst(ffbuf, &ffblk,
FA_DIREC) ? '*' : ' ';
} else if (nskip > 0)
c = findnext(&ffblk) ? 0 : ' ';
#endif
nskip++;
if (c != ' ') {
TTbeep();
nskip = 0;
}
#if UNIX
while ((c = getc(tmpf)) != EOF && c != '\n'
&& c != ' ' && c != '*')
#endif
#if MSDOS
if (c == '*')
fcp = sffbuf;
else {
strncpy(buf, sffbuf, lsav + 1);
cpos = lsav + 1;
fcp = ffblk.ff_name;
}
while (c != 0 && (c = *fcp++) != 0 && c != '*')
#endif
{
if (cpos < nbuf - 1)
buf[cpos++] = c;
}
#if UNIX
if (c == '*')
TTbeep();
#endif
for (n = 0; n < cpos; n++) {
c = buf[n];
if ((c < ' ') && (c != '\n')) {
outstring("^");
++ttcol;
c ^= 0x40;
}
if (c != '\n') {
if (disinp)
TTputc(c);
} else { /* put out <NL> for <ret> */
outstring("<NL>");
ttcol += 3;
}
++ttcol;
}
TTflush();
#if UNIX
rewind(tmpf);
unlink(tmp);
#endif
#endif
} else if ((c == quotec || c == 0x16) && quotef == FALSE) {
quotef = TRUE;
} else {
quotef = FALSE;
if (cpos < nbuf - 1) {
buf[cpos++] = c;
if ((c < ' ') && (c != '\n')) {
outstring("^");
++ttcol;
c ^= 0x40;
}
if (c != '\n') {
if (disinp)
TTputc(c);
} else { /* put out <NL> for <ret> */
outstring("<NL>");
ttcol += 3;
}
++ttcol;
TTflush();
}
}
}
}
/*
* output a string of characters
*
* char *s; string to output
*/
void outstring(char *s)
{
if (disinp)
while (*s)
TTputc(*s++);
}
/*
* output a string of output characters
*
* char *s; string to output
*/
void ostring(char *s)
{
if (discmd)
while (*s)
TTputc(*s++);
}