#include <sys/queue.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <term.h>
#include "def.h"
#include "funmap.h"
#include "key.h"
#include "macro.h"
static char *veread(const char *, char *, size_t, int, va_list)
__attribute__((__format__ (printf, 1, 0)));
static int complt(int, int, char *, size_t, int, int *);
static int complt_list(int, char *, int);
static void eformat(const char *, va_list)
__attribute__((__format__ (printf, 1, 0)));
static void eputi(int, int);
static void eputl(long, int);
static void eputs(const char *);
static void eputc(char);
static struct list *copy_list(struct list *);
int epresf = FALSE;
void
eerase(void)
{
ttcolor(CTEXT);
ttmove(nrow - 1, 0);
tteeol();
ttflush();
epresf = FALSE;
}
int
eyorn(const char *sp)
{
int s;
if (inmacro)
return (TRUE);
ewprintf("%s? (y or n) ", sp);
for (;;) {
s = getkey(FALSE);
if (s == 'y' || s == 'Y' || s == ' ') {
eerase();
return (TRUE);
}
if (s == 'n' || s == 'N' || s == CCHR('M')) {
eerase();
return (FALSE);
}
if (s == CCHR('G')) {
eerase();
return (ctrlg(FFRAND, 1));
}
ewprintf("Please answer y or n. %s? (y or n) ", sp);
}
}
int
eynorr(const char *sp)
{
int s;
if (inmacro)
return (TRUE);
ewprintf("%s? (y, n or r) ", sp);
for (;;) {
s = getkey(FALSE);
if (s == 'y' || s == 'Y' || s == ' ') {
eerase();
return (TRUE);
}
if (s == 'n' || s == 'N' || s == CCHR('M')) {
eerase();
return (FALSE);
}
if (s == 'r' || s == 'R') {
eerase();
return (REVERT);
}
if (s == CCHR('G')) {
eerase();
return (ctrlg(FFRAND, 1));
}
ewprintf("Please answer y, n or r.");
}
}
int
eyesno(const char *sp)
{
char buf[64], *rep;
if (inmacro)
return (TRUE);
rep = eread("%s? (yes or no) ", buf, sizeof(buf),
EFNUL | EFNEW | EFCR, sp);
for (;;) {
if (rep == NULL) {
eerase();
return (ABORT);
}
if (rep[0] != '\0') {
if (macrodef) {
struct line *lp = maclcur;
maclcur = lp->l_bp;
maclcur->l_fp = lp->l_fp;
free(lp);
}
if (strcasecmp(rep, "yes") == 0) {
eerase();
return (TRUE);
}
if (strcasecmp(rep, "no") == 0) {
eerase();
return (FALSE);
}
}
rep = eread("Please answer yes or no. %s? (yes or no) ",
buf, sizeof(buf), EFNUL | EFNEW | EFCR, sp);
}
}
char *
eread(const char *fmt, char *buf, size_t nbuf, int flag, ...)
{
va_list ap;
char *rep;
va_start(ap, flag);
rep = veread(fmt, buf, nbuf, flag, ap);
va_end(ap);
return (rep);
}
static char *
veread(const char *fp, char *buf, size_t nbuf, int flag, va_list ap)
{
int dynbuf = (buf == NULL);
int cpos, epos;
int c, i, y;
int cplflag;
int cwin = FALSE;
int mr, ml;
int esc;
struct buffer *bp;
struct mgwin *wp;
int match;
int cc, rr;
char *ret;
static char emptyval[] = "";
if (inmacro) {
if (dynbuf) {
if ((buf = malloc(maclcur->l_used + 1)) == NULL)
return (NULL);
} else if (maclcur->l_used >= nbuf)
return (NULL);
bcopy(maclcur->l_text, buf, maclcur->l_used);
buf[maclcur->l_used] = '\0';
maclcur = maclcur->l_fp;
return (buf);
}
epos = cpos = 0;
ml = mr = esc = 0;
cplflag = FALSE;
if ((flag & EFNEW) != 0 || ttrow != nrow - 1) {
ttcolor(CTEXT);
ttmove(nrow - 1, 0);
epresf = TRUE;
} else
eputc(' ');
eformat(fp, ap);
if ((flag & EFDEF) != 0) {
if (buf == NULL)
return (NULL);
eputs(buf);
epos = cpos += strlen(buf);
}
tteeol();
ttflush();
for (;;) {
c = getkey(FALSE);
if ((flag & EFAUTO) != 0 && c == CCHR('I')) {
if (cplflag == TRUE) {
complt_list(flag, buf, cpos);
cwin = TRUE;
} else if (complt(flag, c, buf, nbuf, epos, &i) == TRUE) {
cplflag = TRUE;
epos += i;
cpos = epos;
}
continue;
}
cplflag = FALSE;
if (esc > 0) {
match = 0;
if (ml == esc && key_left[ml] && c == key_left[ml]) {
match++;
if (key_left[++ml] == '\0') {
c = CCHR('B');
esc = 0;
}
}
if (mr == esc && key_right[mr] && c == key_right[mr]) {
match++;
if (key_right[++mr] == '\0') {
c = CCHR('F');
esc = 0;
}
}
if (match == 0) {
esc = 0;
continue;
}
if (esc > 0) {
esc++;
continue;
}
}
switch (c) {
case CCHR('A'):
while (cpos > 0) {
if (ISCTRL(buf[--cpos]) != FALSE) {
ttputc('\b');
--ttcol;
}
ttputc('\b');
--ttcol;
}
ttflush();
break;
case CCHR('D'):
if (cpos != epos) {
tteeol();
epos--;
rr = ttrow;
cc = ttcol;
for (i = cpos; i < epos; i++) {
buf[i] = buf[i + 1];
eputc(buf[i]);
}
ttmove(rr, cc);
ttflush();
}
break;
case CCHR('E'):
while (cpos < epos) {
eputc(buf[cpos++]);
}
ttflush();
break;
case CCHR('B'):
if (cpos > 0) {
if (ISCTRL(buf[--cpos]) != FALSE) {
ttputc('\b');
--ttcol;
}
ttputc('\b');
--ttcol;
ttflush();
}
break;
case CCHR('F'):
if (cpos < epos) {
eputc(buf[cpos++]);
ttflush();
}
break;
case CCHR('Y'):
i = 0;
while ((y = kremove(i++)) >= 0 && y != *curbp->b_nlchr) {
int t;
if (dynbuf && epos + 1 >= nbuf) {
void *newp;
size_t newsize = epos + epos + 16;
if ((newp = realloc(buf, newsize))
== NULL)
goto memfail;
buf = newp;
nbuf = newsize;
}
if (!dynbuf && epos + 1 >= nbuf) {
dobeep();
ewprintf("Line too long. Press Control-g to escape.");
goto skipkey;
}
for (t = epos; t > cpos; t--)
buf[t] = buf[t - 1];
buf[cpos++] = (char)y;
epos++;
eputc((char)y);
cc = ttcol;
rr = ttrow;
for (t = cpos; t < epos; t++)
eputc(buf[t]);
ttmove(rr, cc);
}
ttflush();
break;
case CCHR('K'):
kdelete();
for (i = cpos; i < epos; i++)
kinsert(buf[i], KFORW);
tteeol();
epos = cpos;
ttflush();
break;
case CCHR('['):
ml = mr = esc = 1;
break;
case CCHR('J'):
c = CCHR('M');
case CCHR('M'):
if (epos == 0 && !(flag & EFNUL)) {
(void)ctrlg(FFRAND, 0);
ttflush();
return (NULL);
}
if ((flag & EFFUNC) != 0) {
if (complt(flag, c, buf, nbuf, epos, &i)
== FALSE)
continue;
if (i > 0)
epos += i;
}
buf[epos] = '\0';
if ((flag & EFCR) != 0) {
ttputc(CCHR('M'));
ttflush();
}
if (macrodef) {
struct line *lp;
if ((lp = lalloc(cpos)) == NULL)
goto memfail;
lp->l_fp = maclcur->l_fp;
maclcur->l_fp = lp;
lp->l_bp = maclcur;
maclcur = lp;
bcopy(buf, lp->l_text, cpos);
}
ret = buf;
goto done;
case CCHR('G'):
eputc(CCHR('G'));
(void)ctrlg(FFRAND, 0);
ttflush();
ret = NULL;
goto done;
case CCHR('H'):
case CCHR('?'):
if (cpos != 0) {
y = buf[--cpos];
epos--;
ttputc('\b');
ttcol--;
if (ISCTRL(y) != FALSE) {
ttputc('\b');
ttcol--;
}
rr = ttrow;
cc = ttcol;
for (i = cpos; i < epos; i++) {
buf[i] = buf[i + 1];
eputc(buf[i]);
}
ttputc(' ');
if (ISCTRL(y) != FALSE) {
ttputc(' ');
ttputc('\b');
}
ttputc('\b');
ttmove(rr, cc);
ttflush();
}
break;
case CCHR('X'):
case CCHR('U'):
while (cpos != 0) {
ttputc('\b');
ttputc(' ');
ttputc('\b');
--ttcol;
if (ISCTRL(buf[--cpos]) != FALSE) {
ttputc('\b');
ttputc(' ');
ttputc('\b');
--ttcol;
}
epos--;
}
ttflush();
break;
case CCHR('W'):
while ((cpos > 0) && !ISWORD(buf[cpos - 1])) {
ttputc('\b');
ttputc(' ');
ttputc('\b');
--ttcol;
if (ISCTRL(buf[--cpos]) != FALSE) {
ttputc('\b');
ttputc(' ');
ttputc('\b');
--ttcol;
}
epos--;
}
while ((cpos > 0) && ISWORD(buf[cpos - 1])) {
ttputc('\b');
ttputc(' ');
ttputc('\b');
--ttcol;
if (ISCTRL(buf[--cpos]) != FALSE) {
ttputc('\b');
ttputc(' ');
ttputc('\b');
--ttcol;
}
epos--;
}
ttflush();
break;
case CCHR('\\'):
case CCHR('Q'):
c = getkey(FALSE);
default:
if (dynbuf && epos + 1 >= nbuf) {
void *newp;
size_t newsize = epos + epos + 16;
if ((newp = realloc(buf, newsize)) == NULL)
goto memfail;
buf = newp;
nbuf = newsize;
}
if (!dynbuf && epos + 1 >= nbuf) {
dobeep();
ewprintf("Line too long. Press Control-g to escape.");
goto skipkey;
}
for (i = epos; i > cpos; i--)
buf[i] = buf[i - 1];
buf[cpos++] = (char)c;
epos++;
eputc((char)c);
cc = ttcol;
rr = ttrow;
for (i = cpos; i < epos; i++)
eputc(buf[i]);
ttmove(rr, cc);
ttflush();
}
skipkey:
;
}
done:
if (cwin == TRUE) {
bp = bfind("*Completions*", TRUE);
if ((wp = popbuf(bp, WEPHEM)) != NULL) {
if (wp->w_flag & WEPHEM) {
curwp = wp;
delwind(FFRAND, 1);
} else {
killbuffer(bp);
}
}
}
return (ret);
memfail:
if (dynbuf && buf)
free(buf);
dobeep();
ewprintf("Out of memory");
return (emptyval);
}
static int
complt(int flags, int c, char *buf, size_t nbuf, int cpos, int *nx)
{
struct list *lh, *lh2;
struct list *wholelist = NULL;
int i, nxtra, nhits, bxtra, msglen, nshown;
int wflag = FALSE;
char *msg;
lh = lh2 = NULL;
if ((flags & EFFUNC) != 0) {
buf[cpos] = '\0';
wholelist = lh = complete_function_list(buf);
} else if ((flags & EFBUF) != 0) {
lh = &(bheadp->b_list);
} else if ((flags & EFFILE) != 0) {
buf[cpos] = '\0';
wholelist = lh = make_file_list(buf);
} else
panic("broken complt call: flags");
if (c == ' ')
wflag = TRUE;
else if (c != '\t' && c != CCHR('M'))
panic("broken complt call: c");
nhits = 0;
nxtra = HUGE;
for (; lh != NULL; lh = lh->l_next) {
if (strncmp(buf, lh->l_name, cpos) != 0)
continue;
if (nhits == 0)
lh2 = lh;
++nhits;
if (lh->l_name[cpos] == '\0')
nxtra = -1;
else {
bxtra = getxtra(lh, lh2, cpos, wflag);
if (bxtra < nxtra)
nxtra = bxtra;
lh2 = lh;
}
}
if (nhits == 0)
msg = " [No match]";
else if (nhits > 1 && nxtra == 0)
msg = " [Ambiguous. Ctrl-G to cancel]";
else {
if (nxtra < 0 && nhits > 1 && c == ' ')
nxtra = 1;
for (i = 0; i < nxtra && cpos < nbuf; ++i) {
buf[cpos] = lh2->l_name[cpos];
eputc(buf[cpos++]);
}
ttflush();
free_file_list(wholelist);
*nx = nxtra;
if (nxtra < 0 && c != CCHR('M'))
*nx = 0;
return (TRUE);
}
free_file_list(wholelist);
msglen = strlen(msg);
nshown = (ttcol + msglen + 2 > ncol) ?
ncol - ttcol - 2 : msglen;
eputs(msg);
ttcol -= (i = nshown);
while (i--)
ttputc('\b');
ttflush();
i = nshown;
while (i--)
eputc(' ');
ttcol -= (i = nshown);
while (i--)
ttputc('\b');
*nx = nxtra;
return ((nhits > 0) ? TRUE : FALSE);
}
static int
complt_list(int flags, char *buf, int cpos)
{
struct list *lh, *lh2, *lh3;
struct list *wholelist = NULL;
struct buffer *bp;
int i, maxwidth, width;
int preflen = 0;
int oldrow = ttrow;
int oldcol = ttcol;
int oldhue = tthue;
char *linebuf;
size_t linesize, len;
char *cp;
lh = NULL;
ttflush();
bp = bfind("*Completions*", TRUE);
if (bclear(bp) == FALSE)
return (FALSE);
bp->b_flag |= BFREADONLY;
if ((flags & EFBUF) != 0)
wholelist = lh = copy_list(&(bheadp->b_list));
else if ((flags & EFFUNC) != 0) {
buf[cpos] = '\0';
wholelist = lh = complete_function_list(buf);
} else if ((flags & EFFILE) != 0) {
buf[cpos] = '\0';
wholelist = lh = make_file_list(buf);
cp = strrchr(buf, '/');
if (cp)
preflen = cp - buf + 1;
} else
panic("broken complt call: flags");
lh2 = lh;
while (lh2 != NULL) {
lh3 = lh2->l_next;
while (lh3 != NULL) {
if (strcmp(lh2->l_name, lh3->l_name) > 0) {
cp = lh2->l_name;
lh2->l_name = lh3->l_name;
lh3->l_name = cp;
}
lh3 = lh3->l_next;
}
lh2 = lh2->l_next;
}
maxwidth = 0;
lh2 = lh;
while (lh2 != NULL) {
for (i = 0; i < cpos; ++i) {
if (buf[i] != lh2->l_name[i])
break;
}
if (i == cpos) {
width = strlen(lh2->l_name);
if (width > maxwidth)
maxwidth = width;
}
lh2 = lh2->l_next;
}
maxwidth += 1 - preflen;
linesize = (ncol > maxwidth ? ncol : maxwidth) + 1;
if ((linebuf = malloc(linesize)) == NULL) {
free_file_list(wholelist);
return (FALSE);
}
width = 0;
linebuf[0] = '\0';
for (lh2 = lh; lh2 != NULL; lh2 = lh2->l_next) {
for (i = 0; i < cpos; ++i) {
if (buf[i] != lh2->l_name[i])
break;
}
if (i == cpos) {
if ((width + maxwidth) > ncol) {
addline(bp, linebuf);
linebuf[0] = '\0';
width = 0;
}
len = strlcat(linebuf, lh2->l_name + preflen,
linesize);
width += maxwidth;
if (len < width && width < linesize) {
memset(linebuf + len, ' ',
maxwidth - strlen(lh2->l_name + preflen));
linebuf[width] = '\0';
}
}
}
if (width > 0)
addline(bp, linebuf);
free(linebuf);
free_file_list(wholelist);
popbuftop(bp, WEPHEM);
update(CMODE);
ttmove(oldrow, oldcol);
ttcolor(oldhue);
ttflush();
return (0);
}
int
getxtra(struct list *lp1, struct list *lp2, int cpos, int wflag)
{
int i;
i = cpos;
for (;;) {
if (lp1->l_name[i] != lp2->l_name[i])
break;
if (lp1->l_name[i] == '\0')
break;
++i;
if (wflag && !ISWORD(lp1->l_name[i - 1]))
break;
}
return (i - cpos);
}
void
ewprintf(const char *fmt, ...)
{
va_list ap;
if (inmacro)
return;
va_start(ap, fmt);
ttcolor(CTEXT);
ttmove(nrow - 1, 0);
eformat(fmt, ap);
va_end(ap);
tteeol();
ttflush();
epresf = TRUE;
}
static void
eformat(const char *fp, va_list ap)
{
char kname[NKNAME], tmp[100], *cp;
int c;
while ((c = *fp++) != '\0') {
if (c != '%')
eputc(c);
else {
c = *fp++;
switch (c) {
case 'c':
getkeyname(kname, sizeof(kname),
va_arg(ap, int));
eputs(kname);
break;
case 'k':
for (cp = kname, c = 0; c < key.k_count; c++) {
if (c)
*cp++ = ' ';
cp = getkeyname(cp, sizeof(kname) -
(cp - kname) - 1, key.k_chars[c]);
}
eputs(kname);
break;
case 'd':
eputi(va_arg(ap, int), 10);
break;
case 'o':
eputi(va_arg(ap, int), 8);
break;
case 'p':
snprintf(tmp, sizeof(tmp), "%p",
va_arg(ap, void *));
eputs(tmp);
break;
case 's':
eputs(va_arg(ap, char *));
break;
case 'l':
c = *fp++;
switch (c) {
case 'd':
eputl(va_arg(ap, long), 10);
break;
default:
eputc(c);
break;
}
break;
default:
eputc(c);
}
}
}
}
static void
eputi(int i, int r)
{
int q;
if (i < 0) {
eputc('-');
i = -i;
}
if ((q = i / r) != 0)
eputi(q, r);
eputc(i % r + '0');
}
static void
eputl(long l, int r)
{
long q;
if (l < 0) {
eputc('-');
l = -l;
}
if ((q = l / r) != 0)
eputl(q, r);
eputc((int)(l % r) + '0');
}
static void
eputs(const char *s)
{
int c;
while ((c = *s++) != '\0')
eputc(c);
}
static void
eputc(char c)
{
if (ttcol + 2 < ncol) {
if (ISCTRL(c)) {
eputc('^');
c = CCHR(c);
}
ttputc(c);
++ttcol;
}
}
void
free_file_list(struct list *lp)
{
struct list *next;
while (lp) {
next = lp->l_next;
free(lp->l_name);
free(lp);
lp = next;
}
}
static struct list *
copy_list(struct list *lp)
{
struct list *current, *last, *nxt;
last = NULL;
while (lp) {
current = malloc(sizeof(struct list));
if (current == NULL)
goto fail;
current->l_name = strdup(lp->l_name);
if (current->l_name == NULL) {
free(current);
goto fail;
}
current->l_next = last;
last = current;
lp = lp->l_next;
}
return (last);
fail:
for (current = last; current; current = nxt) {
nxt = current->l_next;
free(current->l_name);
free(current);
}
return (NULL);
}