home *** CD-ROM | disk | FTP | other *** search
- #include "jam.h"
- #include "stdlib.h"
-
- /*
- * 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 "def.h"
- #include "key.h"
- #include "macro.h"
- #include "kbd.h"
-
- #define EchoColor /* CHIGH */ CTEXT /* for normal colored prompt stuff */
-
- static void rn_(altmessage,(char *s));
- static int rn_(getxtra, (LIST *lp1, LIST *lp2, int cpos, int wflag));
- static int rn_(veread, (char *fp, char *buf, int nbuf, int flag, va_list *ap));
- static VOID rn_(eformat, (char *fp, va_list *ap));
- static VOID rn_(eputi, (int i, int r));
- static VOID rn_(eputl, (long l, int r));
- static VOID rn_(eputs, (char *s));
- static VOID rn_(eputc, (char c));
- static int rn_(complt, (int flags, char c, char *buf, int cpos, int size));
-
- int epresf = FALSE; /* Stuff in echo line flag. */
- BOOL eprompting = FALSE;
-
- /* JAM - more strings localized to prevent duplicates
- */
- BOOL ealtmsg = FALSE;
- static char *amb = " [Ambiguous]";
- static char *nomatch = " [No match]";
-
- static char preload[NFILEN] = {0}; /* preloaded a prompt (JAM ) */
- static char altmsg[2*NFILEN] = {0}; /* for interruptable prompt (JAM) */
- static char refreshmsg[2*NFILEN] = {0};
- static int refreshoffset = 0;
- static BOOL collectPrompt = FALSE;
- static int altoffset = 0;
-
- /*
- * Erase the echo line.
- */
- VOID eerase()
- {
- if (ealtmsg)
- {
- altmsg[0] = '\0';
- altoffset = 0;
- }
- else
- {
- ttcolor(EchoColor);
- ttmove(nrow-1, 0);
- tteeol();
- ttflush(FALSE);
- }
- 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;
- int result;
-
- if(inmacro)
- return TRUE;
-
- eprompting = TRUE;
- ewprintf("%s? (y or n) ", sp);
- for (;;) {
- s = getkey(FALSE);
- if (s == 'y' || s == 'Y')
- {
- result = TRUE;
- break;
- }
- if (s == 'n' || s == 'N')
- {
- result = FALSE;
- break;
- }
- if (s == CCHR('G'))
- {
- result = ctrlg(FFRAND, 1);
- break;
- }
- if (!handleKchar(s))
- {
- result = ABORT;
- break;
- }
-
- ttbeep();
- ewprintf("Please answer y or n. %s? (y or n) ", sp);
- }
-
- eprompting = FALSE;
- return(result);
- }
-
- /*
- * 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];
-
- if(inmacro)
- return TRUE;
-
- eprompting = TRUE;
- s = ereply("%s? (yes or no) ", buf, sizeof(buf), sp);
- for (;;) {
- if (s == ABORT)
- break;
-
- if (s != FALSE) {
- if (macrodef) {
- LINE *lp = maclcur;
-
- maclcur = lp->l_bp;
- maclcur->l_fp = lp->l_fp;
- free((char *)lp);
- }
-
- if ((buf[0] == 'y' || buf[0] == 'Y')
- && (buf[1] == 'e' || buf[1] == 'E')
- && (buf[2] == 's' || buf[2] == 'S')
- && (buf[3] == '\0'))
- {
- s = TRUE;
- break;
- }
- if ((buf[0] == 'n' || buf[0] == 'N')
- && (buf[1] == 'o' || buf[0] == 'O')
- && (buf[2] == '\0'))
- {
- s = FALSE;
- break;
- }
- }
- if (!handleKchar(s))
- {
- s = ABORT;
- break;
- }
-
- ttbeep();
- s = ereply("Please answer yes or no. %s? (yes or no) ",
- buf, sizeof(buf), sp);
- }
- eprompting = FALSE;
- return(s);
- }
- /*
- * 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.
- */
- int 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;
- }
-
- /*
- * 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).
- */
- /* VARARGS 0 */
- int 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;
- }
-
- static int veread(fp, buf, nbuf, flag, ap)
- char *fp;
- char *buf;
- int nbuf, flag;
- va_list *ap;
- {
- register int cpos;
- register int i;
- register int c;
- int refreshlength;
- BOOL wasvis = IsCaretVis();
-
- SetCaretVis(FALSE);
-
- if(inmacro) {
- bcopy(maclcur->l_text, buf, (size_t)maclcur->l_used);
- buf[maclcur->l_used] = '\0';
- maclcur = maclcur->l_fp;
- return TRUE;
- }
-
- eprompting = TRUE;
- cpos = 0;
- if ((flag&EFNEW)!=0 || ttrow!=nrow-1) {
- ttcolor(EchoColor);
- ttmove(nrow-1, 0);
- epresf = TRUE;
- } else
- eputc(' ');
-
- /* eformat will prime refreshmsg buffer
- */
- eformat(fp, ap);
- refreshlength = strlen(refreshmsg);
-
- /* dump pre-loaded partial response
- */
- AddString(preload);
- preload[0] = '\0';
-
- tteeol();
- ttflush(FALSE);
- for (;;) {
- if (wasvis)
- SetCaretVis(TRUE);
- c = getkey(FALSE);
- SetCaretVis(FALSE);
-
- if ((flag & EFFILE) && ((c == (int)' ')
- || (c == (int)(CCHR('I')))))
- {
- char dbuf[NFILEN];
- register int i;
-
- for (i = 0; i < cpos; i++)
- dbuf[i] = buf[i];
- dbuf[i] = '\0';
- ExtendedFunction(function_name(diredfiles_));
- AddString(dbuf);
- AddKchar(' ');
- continue;
- }
-
-
- if ((flag&EFAUTO) != 0 && (c == (int)' ' ||
- c == (int)CCHR('I')))
- {
- cpos += complt(flag, (char)c, buf, cpos, nbuf);
- continue;
- }
-
- c = handleKchar(c);
- if (!c)
- c = CCHR('G');
-
- switch (c) {
- case CCHR('Z'):
- ttcolor(EchoColor);
- ttmove(nrow-1, 0);
- eerase();
- eputs(refreshmsg);
- break; /* refresh? ignore it*/
-
- case CCHR('J'):
- c = CCHR('M'); /* and continue */
- case CCHR('M'): /* Return, done. */
- if ((flag&EFFUNC) != 0) {
- if ((i = complt(flag, (char)c, buf, cpos, nbuf)) == 0)
- continue;
- if (i > 0)
- cpos += i;
- }
- buf[cpos] = '\0';
- if ((flag&EFCR) != 0) {
- ttputc(CCHR('M'));
- ttflush(FALSE);
- }
- if(macrodef) {
- LINE *lp;
-
- if((lp = lalloc(cpos)) == NULL)
- return FALSE;
- lp->l_fp = maclcur->l_fp;
- maclcur->l_fp = lp;
- lp->l_bp = maclcur;
- maclcur = lp;
- bcopy(buf, lp->l_text, (size_t)cpos);
- }
- goto done;
-
- case CCHR('G'): /* Bell, abort. */
- eputc(CCHR('G'));
- (VOID) ctrlg(FFRAND, 0);
- ttflush(FALSE);
- eprompting = FALSE;
- return ABORT;
-
- case KDELETE:
- case CCHR('H'):
- case CCHR('?'): /* Rubout, erase. */
- if (cpos != 0) {
- ttputc('\b');
- ttputc(' ');
- ttputc('\b');
- --ttcol;
- if (ISCTRL(buf[--cpos]) != FALSE) {
- ttputc('\b');
- ttputc(' ');
- ttputc('\b');
- --ttcol;
- }
- ttflush(FALSE);
- }
- break;
-
- case CCHR('X'): /* C-X */
- case CCHR('U'): /* C-U, kill line. */
- while (cpos != 0) {
- ttputc('\b');
- ttputc(' ');
- ttputc('\b');
- --ttcol;
- if (ISCTRL(buf[--cpos]) != FALSE) {
- ttputc('\b');
- ttputc(' ');
- ttputc('\b');
- --ttcol;
- }
- }
- ttflush(FALSE);
- 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])) {
- ttputc('\b');
- ttputc(' ');
- ttputc('\b');
- --ttcol;
- if (ISCTRL(buf[--cpos]) != FALSE) {
- ttputc('\b');
- ttputc(' ');
- ttputc('\b');
- --ttcol;
- }
- }
- while ((cpos > 0) && ISWORD(buf[cpos - 1])) {
- ttputc('\b');
- ttputc(' ');
- ttputc('\b');
- --ttcol;
- if (ISCTRL(buf[--cpos]) != FALSE) {
- ttputc('\b');
- ttputc(' ');
- ttputc('\b');
- --ttcol;
- }
- }
- ttflush(FALSE);
- 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;
- eputc((char) c);
- ttflush(FALSE);
- }
- }
- memcpy(&refreshmsg[refreshlength], buf, cpos);
- refreshmsg[refreshlength + cpos] = '\0';
- }
- done:
- eprompting = FALSE;
- return buf[0] != '\0';
- }
-
- /*
- * do completion on a list of objects.
- */
- static int complt(flags, c, buf, cpos, nbuf)
- int flags;
- char c;
- register char *buf;
- register int cpos;
- int nbuf;
- {
- register LIST *lh, *lh2;
- int i, nxtra;
- int nhits, bxtra;
- int wflag = FALSE;
- int msglen, nshown;
- char *msg;
- #ifdef WINDOWED
- int cursoron = IsCaretVis();
- #endif
-
- if ((flags&EFFUNC) != 0) {
- buf[cpos] = '\0';
- i = complete_function(buf, c);
- if(i>0) {
- eputs(&buf[cpos]);
- ttflush(FALSE);
- return i;
- }
- switch(i) {
- case -3:
- msg = amb; /* ambiguous */
- break;
- case -2:
- i=0;
- msg = nomatch;
- break;
- case -1:
- case 0:
- return i;
- default:
- msg = " [Internal error]";
- break;
- }
- } else {
- if ((flags&EFBUF) != 0)
- lh = &(bheadp->b_list);
- 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 = HUGEN;
-
- while (lh != NULL) {
- for (i=0; i<cpos; ++i) {
- if (buf[i] != lh->l_name[i])
- break;
- }
- if (i == cpos) {
- if (nhits == 0)
- lh2 = lh;
- ++nhits;
- if (lh->l_name[i] == '\0') nxtra = -1;
- else {
- bxtra = getxtra(lh, lh2, cpos, wflag);
- if (bxtra < nxtra) nxtra = bxtra;
- lh2 = lh;
- }
- }
- lh = lh->l_next;
- }
- if (nhits == 0)
- msg = nomatch;
- else if (nhits > 1 && nxtra == 0)
- msg = amb;
- else { /* Got a match, do it to it */
- /*
- * Being lazy - ought to check length, but all things
- * autocompleted have known types/lengths.
- */
- if (nxtra < 0 && nhits > 1 && c == ' ')
- nxtra = 1;
- for (i = 0; i < nxtra; ++i)
- {
- if (cpos >= nbuf)
- {
- #ifdef WINDOWED
- WindowMessage("Echo line overflow!", FALSE);
- #else
- # ifdef SomeUnix
- printf("Echo line overflow!\n"); /* gross */
- # endif
- ewprintf("Echo line overflow!"); /* gross */
- #endif
- cpos--;
- buf[cpos] = '\0';
- break;
- }
- buf[cpos] = lh2->l_name[cpos];
- eputc(buf[cpos++]);
- }
- ttflush(FALSE);
- if (nxtra < 0 && c != CCHR('M'))
- return 0;
- return nxtra;
- }
- }
- /* Set up backspaces, etc., being mindful of echo line limit
- */
- msglen = strlen(msg);
- nshown = (ttcol + msglen + 2 > ncol) ?
- ncol - ttcol - 2 : msglen;
- #ifdef WINDOWED
- SetCaretVis(FALSE);
- ttflush(TRUE);
- #endif
- eputs(msg);
- ttflush(TRUE);
- sleep(1);
- ttcol -= (i = nshown); /* update ttcol! */
- while (i--) /* move back before msg */
- ttputc('\b');
- ttflush(FALSE); /* display to user */
- i = nshown;
- while (i--) /* blank out on next flush */
- eputc(' ');
- ttcol -= (i = nshown); /* update ttcol on BS's */
- while (i--)
- ttputc('\b'); /* update ttcol again! */
- #ifdef WINDOWED
- SetCaretVis(cursoron);
- ttflush(TRUE);
- #endif
- return 0;
- }
-
- /*
- * 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 LIST *lp1, *lp2;
- int cpos;
- 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;
- 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 ewprintf(va_alist)
- va_dcl
- {
- va_list pvar;
- register char *fp;
-
- if (inmacro)
- return;
-
- va_start(pvar);
- fp = va_arg(pvar, char *);
- if (!ealtmsg)
- {
- ttcolor(EchoColor);
- ttmove(nrow-1, 0);
- }
- eformat(fp, &pvar);
- va_end(pvar);
-
- if (ealtmsg)
- {
- altmessage(altmsg);
- altoffset = 0;
- }
- else
- {
- tteeol();
- ttflush(FALSE);
- }
- epresf = TRUE;
- }
-
- /*
- * Printf style formatting. This is
- * called by both "ewprintf" and "ereply" to provide
- * formatting services to their clients. The 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 *cp;
-
- refreshoffset = 0;
- refreshmsg[0] = '\0';
- collectPrompt = TRUE;
-
- while ((c = *fp++) != '\0') {
- if (c != (char)'%')
- eputc((char)c);
- else {
- c = *fp++;
- switch (c) {
- case 'c':
- (VOID) mykeyname(kname, va_arg(*ap, int));
- eputs(kname);
- break;
-
- case 'k':
- cp = kname;
- for(c=0; c < key.k_count; c++) {
- cp = mykeyname(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((char)c);
- break;
- }
- break;
-
- default:
- eputc((char)c);
- }
- }
- }
- collectPrompt = FALSE;
- }
-
- /*
- * Put integer, in radix "r".
- */
- static VOID eputi(i, r)
- register int i;
- register int r;
- {
- register int q;
-
- if(i<0) {
- eputc('-');
- i = -i;
- }
- if ((q=i/r) != 0)
- eputi(q, r);
- eputc((char)(i%r+'0'));
- }
-
- /*
- * Put long, in radix "r".
- */
- static VOID eputl(l, r)
- register long l;
- register int r;
- {
- register long q;
-
- if(l < 0) {
- eputc('-');
- l = -l;
- }
- if ((q=l/r) != 0)
- eputl(q, r);
- eputc((char)((l%r)+'0'));
- }
-
- /*
- * Put string.
- */
- static VOID eputs(s)
- register char *s;
- {
- register char c;
-
- while ((c = *s++) != '\0')
- eputc(c);
- ttflush(FALSE);
- }
-
- /*
- * Put character. Watch for
- * control characters, and for the line
- * getting too long.
- */
- static VOID eputc(c)
- register char c;
- {
- if ((ttcol+2 < ncol) || ealtmsg)
- {
- if (ISCTRL(c))
- {
- eputc('^');
- c = (char)CCHR(c);
- }
- if (ealtmsg)
- {
- altmsg[altoffset++] = c;
- altmsg[altoffset] = 0;
- }
- else
- {
- ttputc(c);
- ++ttcol;
- if (collectPrompt)
- {
- refreshmsg[refreshoffset++] = c;
- refreshmsg[refreshoffset] = 0;
- }
- }
- }
- }
-
- /* Alternative message dumping which will
- * not mess up current status (for code running during timer
- * callback, for example)
- */
- static void altmessage(s)
- char *s;
- {
- int curcolor;
- int currow, curcol;
-
- if (ttfatal())
- return;
-
- currow = ttrow;
- curcol = ttcol;
- curcolor = tthue;
- ttcolor(EchoColor);
-
- ttmove(nrow - 1, 0);
- tteeol();
- ttputline(s);
-
- ttcolor(curcolor);
- ttmove(currow, curcol);
- ttflush(FALSE);
- }
-
- /* Deal with unexpected chars during echo-line stuff.
- * (ughly!)
- *
- * Attempts to collect events from 'interrupting' commands
- * to be re-entered into input stream later (main.c)
- */
- BOOL handleKchar(c)
- int c;
- {
- int s = c;
- BOOL metakey = FALSE;
-
- if (s == CCHR('['))
- {
- s = getkey(FALSE); /* grab the key and throw it away */
- s |= METABIT;
- metakey = TRUE;
- }
-
- switch (c)
- {
- case KEXTEND: /* silent_extended command */
- PutbackKchar((KCHAR)c); /* put back trigger char... */
- ttflushinput(FALSE); /* and any parameters..*/
- c = 0;
- break;
- default:
- break;
- }
-
- if (metakey)
- {
- PutbackKchar((KCHAR)s);
- ttflushinput(FALSE);
- }
-
- return(c);
- }
-
- /* preload prompt screen
- */
- void epreload(s)
- char *s;
- {
- if (preload_)
- strcpy(preload, s);
- else
- preload[0] = '\0';
- enable_preload();
- }
-
- void erepair()
- {
- if (epresf || eprompting)
- {
- AddKchar(CCHR('Z'));
- ttflush(FALSE);
- }
- }
-
-