home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frozen Fish 1: Amiga
/
FrozenFish-Apr94.iso
/
bbs
/
alib
/
d3xx
/
d352
/
mg.lha
/
MG
/
src.LZH
/
mg
/
echo.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-05-23
|
14KB
|
755 lines
/*
* Echo line reading and writing.
*
* Common routines for reading and writing characters in the echo line area of
* the display screen. Used by the entire known universe.
*/
#include "no_macro.h"
#include "def.h"
#include "key.h"
#ifdef ANSI
#include <stdarg.h>
#else
#include "varargs.h"
#endif
#include "line.h"
#include "buffer.h"
#include "no_metakey.h"
#ifndef NO_MACRO
# include "macro.h"
#endif
int epresf = FALSE; /* Stuff in echo line flag. */
#ifdef LATTICE_50
static char a; /* Sigh */
#endif
#ifdef ANSI
#include <stdlib.h>
#include <string.h>
#else
VOID ewprintf();
#endif
static int veread
PROTO((char *fp, char *buf, int nbuf, int flag, va_list * ap));
static int complt
PROTO((int flags, int c, char *buf, int cpos));
static VOID eformat
PROTO((char *fp, va_list * ap));
static VOID eputi
PROTO((int i, int r));
static VOID eputl
PROTO((long l, int r));
static VOID eputs
PROTO((char *s));
static VOID eputc
PROTO((int c));
static int complete_list
PROTO((struct list * lh, int c, char *buf, int cpos));
static int getxtra
PROTO((register struct list *, register struct list *, int, register int));
/*
* Erase the echo line.
*/
VOID
eerase()
{
#ifndef NO_MACRO
if (inmacro)
return;
#endif
ttcolor(CTEXT);
ttmove(nrow - 1, 0);
tteeol();
ttflush();
epresf = FALSE;
}
/*
* Ask "yes" or "no" question. Return ABORT if the user answers the question
* with the abort ("^G") character. Return FALSE for "no" and TRUE for "yes".
* No formatting services are available. No newline required.
*/
eyorn(sp)
char *sp;
{
register int s;
ewprintf("%s? (y or n) ", sp);
for (;;) {
s = getkey(DISSCR);
if (s == 'y' || s == 'Y')
return TRUE;
if (s == 'n' || s == 'N')
return FALSE;
if (s == CCHR('G'))
return ctrlg(FFRAND, 1);
ewprintf("Please answer y or n. %s? (y or n) ", sp);
}
/* NOTREACHED */
}
/*
* Like eyorn, but for more important question. User must type either all of
* "yes" or "no", and the trainling newline.
*/
eyesno(sp)
char *sp;
{
register int s;
char buf[64];
s = ereply("%s? (yes or no) ", buf, sizeof(buf), sp);
for (;;) {
if (s == ABORT)
return ABORT;
if (s != FALSE) {
if ((buf[0] == 'y' || buf[0] == 'Y')
&& (buf[1] == 'e' || buf[1] == 'E')
&& (buf[2] == 's' || buf[2] == 'S')
&& (buf[3] == '\0'))
return TRUE;
if ((buf[0] == 'n' || buf[0] == 'N')
&& (buf[1] == 'o' || buf[0] == 'O')
&& (buf[2] == '\0'))
return FALSE;
}
s = ereply("Please answer yes or no. %s? (yes or no) ",
buf, sizeof(buf), sp);
}
/* NOTREACHED */
}
/*
* Write out a prompt, and read back a reply. The prompt is now written out
* with full "ewprintf" formatting, although the arguments are in a rather
* strange place. This is always a new message, there is no auto completion,
* and the return is echoed as such.
*/
#ifndef ANSI
/* VARARGS 0 */
ereply(va_alist)
va_dcl
{
va_list pvar;
register char *fp, *buf;
register int nbuf;
register int i;
va_start(pvar);
fp = va_arg(pvar, char *);
buf = va_arg(pvar, char *);
nbuf = va_arg(pvar, int);
i = veread(fp, buf, nbuf, EFNEW | EFCR, &pvar);
va_end(pvar);
return i;
}
#else
int
#ifdef LATTICE
__stdargs
#endif
ereply(char *fp, char *buf, int nbuf,...)
{
va_list pvar;
register int i;
va_start(pvar, nbuf);
i = veread(fp, buf, nbuf, EFNEW | EFCR, &pvar);
va_end(pvar);
return i;
}
#endif
/*
* This is the general "read input from the echo line" routine. The basic
* idea is that the prompt string "prompt" is written to the echo line, and a
* one line reply is read back into the supplied "buf" (with maximum length
* "len"). The "flag" contains EFNEW (a new prompt), an EFFUNC
* (autocomplete), or EFCR (echo the carriage return as CR).
*/
#ifndef ANSI
/* VARARGS 0 */
eread(va_alist)
va_dcl
{
va_list pvar;
char *fp, *buf;
int nbuf, flag, i;
va_start(pvar);
fp = va_arg(pvar, char *);
buf = va_arg(pvar, char *);
nbuf = va_arg(pvar, int);
flag = va_arg(pvar, int);
i = veread(fp, buf, nbuf, flag, &pvar);
va_end(pvar);
return i;
}
#else
int
#ifdef LATTICE
__stdargs
#endif
eread(char *fp, char *buf, int nbuf, int flag,...)
{
va_list pvar;
int i;
va_start(pvar, flag);
i = veread(fp, buf, nbuf, flag, &pvar);
va_end(pvar);
return i;
}
#endif
static
veread(fp, buf, nbuf, flag, ap)
char *fp;
char *buf;
va_list *ap;
{
register int cpos;
register int i;
register int c;
#ifndef NO_METAKEY
extern int use_metakey;
int save_metakey = use_metakey;
use_metakey = FALSE;
#endif
cpos = 0;
#ifndef NO_MACRO
if (!inmacro) {
#endif
if ((flag & EFNEW) == 0 && ttrow == nrow - 1)
eputc(' ');
else {
ttcolor(CTEXT);
ttmove(nrow - 1, 0);
epresf = TRUE;
}
eformat(fp, ap);
tteeol();
ttflush();
#ifndef NO_MACRO
}
#endif
for (;;) {
c = getkey(FALSE);
if ((flag & EFAUTO) != 0 && (c == ' ' || c == CCHR('I'))) {
cpos += complt(flag, c, buf, cpos);
continue;
}
switch (c) {
case CCHR('J'):
c = CCHR('M'); /* and continue */
case CCHR('M'):/* Return, done. */
if ((flag & EFFORCE) == 0)
buf[cpos] = '\0';
else if ((i = complt(flag, c, buf, cpos)) >= 0) {
cpos += i;
continue;
}
if ((flag & EFCR) != 0
#ifndef NO_MACRO
&& inmacro
#endif
) {
ttputc(CCHR('M'));
ttflush();
}
#ifndef NO_METAKEY
use_metakey = save_metakey;
#endif
return buf[0] != '\0';
case CCHR('G'):/* Bell, abort. */
#ifndef NO_METAKEY
use_metakey = save_metakey;
#endif
#ifndef NO_MACRO
if (inmacro)
return ABORT;
#endif
eputc(CCHR('G'));
(VOID) ctrlg(FFRAND, 0);
ttflush();
return ABORT;
case CCHR('H'):
case CCHR('?'):/* Rubout, erase. */
if (cpos != 0) {
#ifndef NO_MACRO
if (inmacro) {
cpos -= 1;
break;
}
#endif
ttputc('\b');
ttputc(' ');
ttputc('\b');
--ttcol;
if (ISCTRL(buf[--cpos]) != FALSE) {
ttputc('\b');
ttputc(' ');
ttputc('\b');
--ttcol;
}
ttflush();
}
break;
case CCHR('X'):/* C-X */
case CCHR('U'):/* C-U, kill line. */
#ifndef NO_MACRO
if (inmacro) {
cpos = 0;
break;
}
#endif
while (cpos != 0) {
ttputc('\b');
ttputc(' ');
ttputc('\b');
--ttcol;
if (ISCTRL(buf[--cpos]) != FALSE) {
ttputc('\b');
ttputc(' ');
ttputc('\b');
--ttcol;
}
}
ttflush();
break;
case CCHR('W'):/* C-W, kill to beginning of */
/* previous word */
/* back up to first word character or beginning */
while ((cpos > 0) && !ISWORD(buf[cpos - 1])) {
#ifndef NO_MACRO
if (inmacro) {
cpos -= 1;
continue;
}
#endif
ttputc('\b');
ttputc(' ');
ttputc('\b');
--ttcol;
if (ISCTRL(buf[--cpos]) != FALSE) {
ttputc('\b');
ttputc(' ');
ttputc('\b');
--ttcol;
}
}
while ((cpos > 0) && ISWORD(buf[cpos - 1])) {
#ifndef NO_MACRO
if (inmacro) {
cpos -= 1;
continue;
}
#endif
ttputc('\b');
ttputc(' ');
ttputc('\b');
--ttcol;
if (ISCTRL(buf[--cpos]) != FALSE) {
ttputc('\b');
ttputc(' ');
ttputc('\b');
--ttcol;
}
}
#ifndef NO_MACRO
if (!inmacro)
#endif
ttflush();
break;
case CCHR('\\'):
case CCHR('Q'):/* C-Q, quote next */
c = getkey(FALSE); /* and continue */
default: /* All the rest. */
if (cpos < nbuf - 1) {
buf[cpos++] = (char) c;
#ifndef NO_MACRO
if (inmacro)
break;
#endif
eputc((char) c);
ttflush();
}
}
}
}
/*
* do completion for veread. The completion routines return: >= 0 number of
* characters we can complete by -1 no match at all -2 ambiguous -3 match
* is correct & complete as it is, used only if c = C-m
*/
static int
complt(flags, c, buf, cpos)
register char *buf;
register int cpos;
{
register int i, j;
int msglen, nshown;
char *msg;
#ifndef NO_MACRO
register int xtra;
char macbuf[NMACN];
extern struct macro kbdmacro;
#endif
if (flags & EFBUF)
i = complete_list(&(bheadp->b_list), c, buf, cpos);
#ifndef NO_MACRO
else if (flags & EFMACRO)
i = complete_list(&(kbdmacro.m_list), c, buf, cpos);
#endif
else if (flags & EFFUNC) {
buf[cpos] = '\0';
i = complete_function(buf, c);
#ifndef NO_MACRO
strncpy(macbuf, buf, NMACN);
j = complete_list(&(kbdmacro.m_list), c, macbuf, cpos);
if (i == -3 || j == -3)
i = -3;
else if (i == -2 || j == -2)
i = -2;
else if (i == -1) {
if (j > 0)
bcopy(macbuf + cpos, buf + cpos, j + 1);
i = j;
} else if (j >= 0) {
for (xtra = 0; xtra < i & xtra < j; xtra += 1)
if (buf[cpos + xtra] != macbuf[cpos + xtra])
break;
i = xtra == 0 ? -2 : xtra;
buf[cpos + xtra + 1] = '\0';
}
#endif
} else
panic("broken complt call: flags");
#ifndef NO_MACRO
if (inmacro)
return i;
#endif
if (i > 0) {
eputs(&buf[cpos]);
ttflush();
return i;
}
switch (i) {
case -1:
msg = " [No match]";
break;
case -2:
msg = " [Ambiguous]";
break;
case -3:
case 0:
return i;
default:
msg = " [Internal error]";
break;
}
/* Set up backspaces, etc., being mindful of echo line limit */
msglen = strlen(msg);
nshown = (ttcol + msglen + 2 > ncol) ?
ncol - ttcol - 2 : msglen;
eputs(msg);
ttcol -= (j = nshown); /* update ttcol! */
while (j--) /* move back before msg */
ttputc('\b');
ttflush(); /* display to user */
j = nshown;
while (j--) /* blank out on next flush */
eputc(' ');
ttcol -= (j = nshown); /* update ttcol on BS's */
while (j--)
ttputc('\b'); /* update ttcol again! */
return 0;
}
/*
* do completion on a list of objects
*/
static int
complete_list(lh, c, buf, cpos)
register struct list *lh;
register char *buf;
register int cpos;
{
register struct list *lh2;
register int i;
int nhits, nxtra, bxtra;
int match = FALSE;
nhits = 0;
nxtra = HUGE;
while (lh != NULL) {
for (i = 0; i < cpos; i += 1) {
if (buf[i] != lh->l_name[i])
break;
}
if (i == cpos) {
if (nhits == 0)
lh2 = lh;
nhits += 1;
if (lh->l_name[i] == '\0')
match = TRUE;
bxtra = getxtra(lh, lh2, cpos, c == ' ');
if (bxtra < nxtra)
nxtra = bxtra;
lh2 = lh;
}
lh = lh->l_next;
}
if (match && c == CCHR('M'))
return -3; /* complete match */
if (nhits == 0)
return -1; /* No match */
if (nhits > 1 && nxtra == 0)
return -2; /* Ambiguous */
/* Match; copy it out and return the count */
for (i = 0; i < nxtra; i += 1) {
buf[cpos + i] = lh2->l_name[cpos + i];
}
buf[cpos + i] = '\0';
return nxtra;
}
/*
* The "lp1" and "lp2" point to list structures. The "cpos" is a horizontal
* position in the name. Return the longest block of characters that can be
* autocompleted at this point. Sometimes the two symbols are the same, but
* this is normal.
*/
static int
getxtra(lp1, lp2, cpos, wflag)
register struct list *lp1, *lp2;
register int wflag;
{
register int i;
i = cpos;
for (;;) {
if (lp1->l_name[i] != lp2->l_name[i])
break;
if (lp1->l_name[i] == '\0')
break;
i += 1;
if (wflag && !ISWORD(lp1->l_name[i - 1]))
break;
}
return (i - cpos);
}
/*
* Special "printf" for the echo line. Each call to "ewprintf" starts a new
* line in the echo area, and ends with an erase to end of the echo line. The
* formatting is done by a call to the standard formatting routine.
*/
/* VARARGS 0 */
VOID
#ifndef ANSI
ewprintf(va_alist)
va_dcl
#else
#ifdef LATTICE
__stdargs
#endif
ewprintf(char *fp,...)
#endif
{
va_list pvar;
#ifndef ANSI
register char *fp;
#endif
#ifndef NO_MACRO
if (inmacro)
return;
#endif
#ifndef ANSI
va_start(pvar);
fp = va_arg(pvar, char *);
#else
va_start(pvar, fp);
#endif
ttcolor(CTEXT);
ttmove(nrow - 1, 0);
eformat(fp, &pvar);
va_end(pvar);
tteeol();
ttflush();
epresf = TRUE;
}
/*
* Printf style formatting. This is called by both "ewprintf" and "ereply" to
* provide formatting services to their clients. They move to the start of
* the echo line, and the erase to the end of the echo line, is done by the
* caller. Note: %c works, and prints the "name" of the character. %k prints
* the name of a key (and takes no arguments).
*/
static VOID
eformat(fp, ap)
register char *fp;
register va_list *ap;
{
register int c;
char kname[NKNAME];
char *keyname();
char *cp;
#ifndef NO_MACRO
if (inmacro)
return;
#endif
while ((c = *fp++) != '\0') {
if (c != '%')
eputc(c);
else {
c = *fp++;
switch (c) {
case 'c':
(VOID) keyname(kname, va_arg(*ap, int));
eputs(kname);
break;
case 'k':
cp = kname;
for (c = 0; c < key.k_count; c++) {
cp = keyname(cp, key.k_chars[c]);
*cp++ = ' ';
}
*--cp = '\0';
eputs(kname);
break;
case 'd':
eputi(va_arg(*ap, int), 10);
break;
case 'o':
eputi(va_arg(*ap, int), 8);
break;
case 's':
eputs(va_arg(*ap, char *));
break;
case 'l': /* explicit longword */
c = *fp++;
switch (c) {
case 'd':
eputl((long) va_arg(*ap, long), 10);
break;
default:
eputc(c);
break;
}
break;
default:
eputc(c);
}
}
}
}
/*
* Put integer, in radix "r".
*/
static VOID
eputi(i, r)
register int i;
register int r;
{
register int q;
#ifndef NO_MACRO
if (inmacro)
return;
#endif
if (i < 0) {
eputc('-');
i = -i;
}
if ((q = i / r) != 0)
eputi(q, r);
eputc(i % r + '0');
}
/*
* Put long, in radix "r".
*/
static VOID
eputl(l, r)
register long l;
register int r;
{
register long q;
#ifndef NO_MACRO
if (inmacro)
return;
#endif
if (l < 0) {
eputc('-');
l = -l;
}
if ((q = l / r) != 0)
eputl(q, r);
eputc(l % r + '0');
}
/*
* Put string.
*/
static VOID
eputs(s)
register char *s;
{
register int c;
#ifndef NO_MACRO
if (inmacro)
return;
#endif
while ((c = *s++) != '\0')
eputc(c);
}
/*
* Put character. Watch for control characters, and for the line getting too
* long.
*/
static VOID
eputc(c)
register int c;
{
#ifndef NO_MACRO
if (inmacro)
return;
#endif
if (ttcol + 2 < ncol) {
if (ISCTRL(c)) {
eputc('^');
c = CCHR(c);
}
ttputc((char) c);
++ttcol;
}
}