mirror of
https://github.com/rfivet/uemacs.git
synced 2024-12-18 07:16:23 -05:00
d6e76cca7b
Fix the following warning: input.c: In function ‘getstring’: input.c:590: warning: ignoring return value of ‘mkstemp’, declared with attribute warn_unused_result This add usage.c module for die function. This also add wrapper.c module for the xmkstemp that is wrapper function around the original mkstemp function. Both module codes was largelly based on git, linux and sparse codes. Signed-off-by: Thiago Farina <tfransosi@gmail.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
722 lines
14 KiB
C
722 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"
|
|
#include "wrapper.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)
|
|
{
|
|
int cpos; /* current column on screen output */
|
|
int c;
|
|
char *sp; /* pointer to string for output */
|
|
struct name_bind *ffp; /* first ptr to entry in name binding table */
|
|
struct name_bind *cffp; /* current ptr to entry in name binding table */
|
|
struct name_bind *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)
|
|
{
|
|
int cpos; /* current character position in string */
|
|
int c;
|
|
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, " >");
|
|
xmkstemp(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++);
|
|
}
|