diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 2000-02-25 19:08:53 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 2000-02-25 19:08:53 +0000 |
commit | 998d769a0cf8bef7d4ca0d26945c151a23b542ec (patch) | |
tree | fe53a083eaa06a2bf7631453e18a161a86ad9d62 /usr.bin/mg/echo.c | |
parent | b0226ecd4460819556afd27fd575d64421fd0f68 (diff) |
initial import of mg2a
Diffstat (limited to 'usr.bin/mg/echo.c')
-rw-r--r-- | usr.bin/mg/echo.c | 781 |
1 files changed, 781 insertions, 0 deletions
diff --git a/usr.bin/mg/echo.c b/usr.bin/mg/echo.c new file mode 100644 index 00000000000..3663cda638c --- /dev/null +++ b/usr.bin/mg/echo.c @@ -0,0 +1,781 @@ +/* + * 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. + */ +/* + * The varargs lint directive comments are 0 an attempt to get lint to shup + * up about CORRECT usage of varargs.h. It won't. + */ +#include "def.h" +#include "key.h" +#ifdef LOCAL_VARARGS +#include "varargs.h" +#else +#include <varargs.h> +#endif +#ifndef NO_MACRO +# include "macro.h" +#endif + +static int veread(); +VOID ewprintf(); +static VOID eformat(); +static VOID eputi(); +static VOID eputl(); +static VOID eputs(); +static VOID eputc(); +static int complt(); +static int complt_list(); +LIST * make_file_list(); +LIST * copy_list(); +char * strrchr(); +extern LIST * complete_function_list(); + +int epresf = FALSE; /* Stuff in echo line flag. */ + +extern int tthue; + +/* + * Erase the echo line. + */ +VOID +eerase() { + 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; + +#ifndef NO_MACRO + if(inmacro) return TRUE; +#endif + ewprintf("%s? (y or n) ", sp); + for (;;) { + s = getkey(FALSE); + 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]; + +#ifndef NO_MACRO + if(inmacro) return TRUE; +#endif + s = ereply("%s? (yes or no) ", buf, sizeof(buf), sp); + for (;;) { + if (s == ABORT) return ABORT; + if (s != FALSE) { +#ifndef NO_MACRO + if (macrodef) { + LINE *lp = maclcur; + + maclcur = lp->l_bp; + maclcur->l_fp = lp->l_fp; + free((char *)lp); + } +#endif + 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. + */ +/*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; +} + +/* + * 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 */ +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 veread(fp, buf, nbuf, flag, ap) char *fp; char *buf; va_list *ap; { + register int cpos; + register int i; + register int c; + +#ifndef NO_MACRO + if(inmacro) { + bcopy(maclcur->l_text, buf, maclcur->l_used); + buf[maclcur->l_used] = '\0'; + maclcur = maclcur->l_fp; + return TRUE; + } +#endif + cpos = 0; + if ((flag&EFNEW)!=0 || ttrow!=nrow-1) { + ttcolor(CTEXT); + ttmove(nrow-1, 0); + epresf = TRUE; + } else + eputc(' '); + eformat(fp, ap); + tteeol(); + ttflush(); + for (;;) { + c = getkey(FALSE); + if ((flag&EFAUTO) != 0 && (c == ' ' || c == CCHR('I'))) { + cpos += complt(flag, c, buf, cpos); + continue; + } + if ((flag&EFAUTO) != 0 && c == '?') { + complt_list(flag, c, buf, cpos); + continue; + } + switch (c) { + case CCHR('J'): + c = CCHR('M'); /* and continue */ + case CCHR('M'): /* Return, done. */ + if ((flag&EFFUNC) != 0) { + if ((i = complt(flag, c, buf, cpos)) == 0) + continue; + if (i > 0) cpos += i; + } + buf[cpos] = '\0'; + if ((flag&EFCR) != 0) { + ttputc(CCHR('M')); + ttflush(); + } +#ifndef NO_MACRO + 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, cpos); + } +#endif + goto done; + + case CCHR('G'): /* Bell, abort. */ + eputc(CCHR('G')); + (VOID) ctrlg(FFRAND, 0); + ttflush(); + return ABORT; + + 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(); + } + 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(); + 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(); + 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(); + } + } + } +done: return buf[0] != '\0'; +} + +/* + * do completion on a list of objects. + */ +static int complt(flags, c, buf, cpos) +register char *buf; +register int cpos; +{ + register LIST *lh, *lh2; + LIST *wholelist = NULL; + int i, nxtra; + int nhits, bxtra; + int wflag = FALSE; + int msglen, nshown; + char *msg; + + if ((flags&EFFUNC) != 0) { + buf[cpos] = '\0'; + i = complete_function(buf, c); + if(i>0) { + eputs(&buf[cpos]); + ttflush(); + return i; + } + switch(i) { + case -3: + msg = " [Ambiguous]"; + break; + case -2: + i=0; + msg = " [No match]"; + break; + case -1: + case 0: + return i; + default: + msg = " [Internal error]"; + break; + } + } else { + if ((flags&EFBUF) != 0) lh = &(bheadp->b_list); + else if ((flags&EFFILE) != 0) { + buf[cpos] = '\0'; + wholelist = lh = make_file_list(buf,0); + } + 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; + + 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 = " [No match]"; + else if (nhits > 1 && nxtra == 0) + msg = " [Ambiguous]"; + 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) { + buf[cpos] = lh2->l_name[cpos]; + eputc(buf[cpos++]); + } + ttflush(); + free_file_list(wholelist); + if (nxtra < 0 && c != CCHR('M')) return 0; + return nxtra; + } + } + /* wholelist is null if we are doing buffers. want to free + * lists that were created for us, but not the buffer list! */ + free_file_list(wholelist); + /* 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 -= (i = nshown); /* update ttcol! */ + while (i--) /* move back before msg */ + ttputc('\b'); + ttflush(); /* 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! */ + return 0; +} + +/* + * do completion on a list of objects, listing instead of completing + */ +static int complt_list(flags, c, buf, cpos) +register char *buf; +register int cpos; +{ + register LIST *lh, *lh2, *lh3; + LIST *wholelist = NULL; + int i,maxwidth,width; + int preflen = 0; + BUFFER *bp; + static VOID findbind(); + int oldrow = ttrow; + int oldcol = ttcol; + int oldhue = tthue; + char linebuf[NCOL+1]; + char *cp; + + ttflush(); + + /* the results are put into a help buffer */ + + bp = bfind("*help*", TRUE); + if(bclear(bp) == FALSE) return FALSE; + + { /* this {} present for historical reasons */ + +/* + * first get the list of objects. This list may contain only the + * ones that complete what has been typed, or may be the whole list + * of all objects of this type. They are filtered later in any case. + * set wholelist if the list has been cons'ed up just for us, so we + * can free it later. We have to copy the buffer list for this + * function even though we didn't for complt. The sorting code + * does destructive changes to the list, which we don't want to + * happen to the main buffer list! + */ + 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, c); + } + else if ((flags&EFFILE) != 0) { + buf[cpos] = '\0'; + wholelist = lh = make_file_list(buf,1); + /* + * we don't want to display stuff up to the / for file names + * preflen is the list of a prefix of what the user typed + * that should not be displayed. + */ + cp = strrchr(buf,'/'); + if (cp) + preflen = cp - buf + 1; + } + else panic("broken complt call: flags"); + + +/* sort the list, since users expect to see it in alphabetic order */ + + lh2 = lh; + while (lh2) { + lh3 = lh2->l_next; + while (lh3) { + 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; + } + +/* + * first find max width of object to be displayed, so we can + * put several on a line + */ + 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; + +/* + * now do the display. objects are written into linebuf until it + * fills, and then put into the help buffer. + */ + cp = linebuf; + width = 0; + lh2 = lh; + while (lh2 != NULL) { + for (i=0; i<cpos; ++i) { + if (buf[i] != lh2->l_name[i]) + break; + } + if (i == cpos) { + if ((width + maxwidth) > ncol) { + *cp = 0; + addline(bp,linebuf); + cp = linebuf; + width = 0; + } + strcpy(cp,lh2->l_name+preflen); + i = strlen(lh2->l_name+preflen); + cp += i; + for (; i < maxwidth; i++) + *cp++ = ' '; + width += maxwidth; + } + lh2 = lh2->l_next; + } + if (width > 0) { + *cp = 0; + addline(bp,linebuf); + } + } + /* + * note that we free lists only if they are put in wholelist + * lists that were built just for us should be freed. However + * when we use the buffer list, obviously we don't want it + * freed. + */ + free_file_list(wholelist); + popbuftop(bp); /* split the screen and put up the help buffer */ + update(); /* needed to make the new stuff actually appear */ + ttmove(oldrow,oldcol); /* update leaves cursor in arbitrary place */ + ttcolor(oldhue); /* with arbitrary color */ + ttflush(); + 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. + */ +getxtra(lp1, lp2, cpos, wflag) register 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; + 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; + +#ifndef NO_MACRO + if(inmacro) return; +#endif + va_start(pvar); + fp = va_arg(pvar, char *); + 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. 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 *keyname(); + char *cp; + + 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; + + 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; + + if(l < 0) { + eputc('-'); + l = -l; + } + if ((q=l/r) != 0) + eputl(q, r); + eputc((int)(l%r)+'0'); +} + +/* + * Put string. + */ +static VOID +eputs(s) +register char *s; +{ + register int c; + + while ((c = *s++) != '\0') + eputc(c); +} + +/* + * Put character. Watch for + * control characters, and for the line + * getting too long. + */ +static VOID +eputc(c) +register char c; +{ + if (ttcol+2 < ncol) { + if (ISCTRL(c)) { + eputc('^'); + c = CCHR(c); + } + ttputc(c); + ++ttcol; + } +} + +free_file_list(lp) + LIST *lp; +{ +LIST *next; +while (lp) { + next = lp->l_next; + free(lp); + lp = next; +} +} + +LIST *copy_list(lp) + LIST *lp; +{ +LIST *current,*last; + +last = NULL; +while(lp) { + current = (LIST *)malloc(sizeof(LIST)); + current->l_next = last; + current->l_name = lp->l_name; + last = (LIST *)current; + lp = lp->l_next; +} +return(last); +} |