[BACK]Return to echo.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / mg

Annotation of src/usr.bin/mg/echo.c, Revision 1.69

1.69    ! op          1: /*     $OpenBSD: echo.c,v 1.68 2021/03/02 15:03:35 lum Exp $   */
1.35      kjell       2:
                      3: /* This file is in the public domain. */
                      4:
1.1       deraadt     5: /*
1.3       millert     6:  *     Echo line reading and writing.
1.1       deraadt     7:  *
1.3       millert     8:  * Common routines for reading and writing characters in the echo line area
                      9:  * of the display screen. Used by the entire known universe.
1.1       deraadt    10:  */
1.3       millert    11:
1.58      bcallah    12: #include <sys/queue.h>
                     13: #include <signal.h>
                     14: #include <stdarg.h>
                     15: #include <stdio.h>
                     16: #include <stdlib.h>
                     17: #include <string.h>
                     18: #include <term.h>
                     19:
1.3       millert    20: #include "def.h"
1.58      bcallah    21: #include "funmap.h"
1.3       millert    22: #include "key.h"
                     23: #include "macro.h"
1.1       deraadt    24:
1.60      guenther   25: static char    *veread(const char *, char *, size_t, int, va_list)
                     26:                        __attribute__((__format__ (printf, 1, 0)));
1.38      kjell      27: static int      complt(int, int, char *, size_t, int, int *);
1.41      kjell      28: static int      complt_list(int, char *, int);
1.60      guenther   29: static void     eformat(const char *, va_list)
1.66      jasper     30:                        __attribute__((__format__ (printf, 1, 0)));
1.31      db         31: static void     eputi(int, int);
                     32: static void     eputl(long, int);
                     33: static void     eputs(const char *);
                     34: static void     eputc(char);
1.40      deraadt    35: static struct list     *copy_list(struct list *);
1.1       deraadt    36:
1.14      mickey     37: int            epresf = FALSE;         /* stuff in echo line flag */
1.1       deraadt    38:
                     39: /*
                     40:  * Erase the echo line.
                     41:  */
1.10      art        42: void
1.21      vincent    43: eerase(void)
1.2       millert    44: {
1.1       deraadt    45:        ttcolor(CTEXT);
1.2       millert    46:        ttmove(nrow - 1, 0);
1.1       deraadt    47:        tteeol();
                     48:        ttflush();
                     49:        epresf = FALSE;
                     50: }
                     51:
                     52: /*
1.14      mickey     53:  * Ask a "yes" or "no" question.  Return ABORT if the user answers the
                     54:  * question with the abort ("^G") character.  Return FALSE for "no" and
                     55:  * TRUE for "yes".  No formatting services are available.  No newline
1.3       millert    56:  * required.
1.1       deraadt    57:  */
1.2       millert    58: int
1.21      vincent    59: eyorn(const char *sp)
1.2       millert    60: {
1.3       millert    61:        int      s;
1.1       deraadt    62:
1.2       millert    63:        if (inmacro)
1.31      db         64:                return (TRUE);
1.50      lum        65:
1.1       deraadt    66:        ewprintf("%s? (y or n) ", sp);
                     67:        for (;;) {
                     68:                s = getkey(FALSE);
1.64      lum        69:                if (s == 'y' || s == 'Y' || s == ' ') {
1.69    ! op         70:                        eerase();
1.31      db         71:                        return (TRUE);
1.64      lum        72:                }
                     73:                if (s == 'n' || s == 'N' || s == CCHR('M')) {
1.69    ! op         74:                        eerase();
1.31      db         75:                        return (FALSE);
1.64      lum        76:                }
                     77:                if (s == CCHR('G')) {
1.69    ! op         78:                        eerase();
1.31      db         79:                        return (ctrlg(FFRAND, 1));
1.64      lum        80:                }
1.1       deraadt    81:                ewprintf("Please answer y or n.  %s? (y or n) ", sp);
1.52      florian    82:        }
                     83:        /* NOTREACHED */
                     84: }
                     85:
                     86: /*
                     87:  * Ask a "yes", "no" or "revert" question.  Return ABORT if the user answers
                     88:  * the question with the abort ("^G") character.  Return FALSE for "no",
                     89:  * TRUE for "yes" and REVERT for "revert". No formatting services are
                     90:  * available.  No newline required.
                     91:  */
                     92: int
                     93: eynorr(const char *sp)
                     94: {
                     95:        int      s;
                     96:
                     97:        if (inmacro)
                     98:                return (TRUE);
                     99:
                    100:        ewprintf("%s? (y, n or r) ", sp);
                    101:        for (;;) {
                    102:                s = getkey(FALSE);
1.64      lum       103:                if (s == 'y' || s == 'Y' || s == ' ') {
1.69    ! op        104:                        eerase();
1.52      florian   105:                        return (TRUE);
1.64      lum       106:                }
                    107:                if (s == 'n' || s == 'N' || s == CCHR('M')) {
1.69    ! op        108:                        eerase();
1.52      florian   109:                        return (FALSE);
1.64      lum       110:                }
                    111:                if (s == 'r' || s == 'R') {
1.69    ! op        112:                        eerase();
1.52      florian   113:                        return (REVERT);
1.64      lum       114:                }
                    115:                if (s == CCHR('G')) {
1.69    ! op        116:                        eerase();
1.52      florian   117:                        return (ctrlg(FFRAND, 1));
1.64      lum       118:                }
1.52      florian   119:                ewprintf("Please answer y, n or r.");
1.1       deraadt   120:        }
1.2       millert   121:        /* NOTREACHED */
1.1       deraadt   122: }
                    123:
                    124: /*
1.3       millert   125:  * Like eyorn, but for more important questions.  User must type all of
1.31      db        126:  * "yes" or "no" and the trailing newline.
1.1       deraadt   127:  */
1.2       millert   128: int
1.21      vincent   129: eyesno(const char *sp)
1.2       millert   130: {
1.29      vincent   131:        char     buf[64], *rep;
1.1       deraadt   132:
1.2       millert   133:        if (inmacro)
1.31      db        134:                return (TRUE);
1.50      lum       135:
1.36      kjell     136:        rep = eread("%s? (yes or no) ", buf, sizeof(buf),
                    137:            EFNUL | EFNEW | EFCR, sp);
1.1       deraadt   138:        for (;;) {
1.64      lum       139:                if (rep == NULL) {
1.69    ! op        140:                        eerase();
1.31      db        141:                        return (ABORT);
1.64      lum       142:                }
1.29      vincent   143:                if (rep[0] != '\0') {
1.1       deraadt   144:                        if (macrodef) {
1.40      deraadt   145:                                struct line     *lp = maclcur;
1.1       deraadt   146:
1.2       millert   147:                                maclcur = lp->l_bp;
                    148:                                maclcur->l_fp = lp->l_fp;
1.43      kjell     149:                                free(lp);
1.1       deraadt   150:                        }
1.65      jsg       151:                        if (strcasecmp(rep, "yes") == 0) {
1.69    ! op        152:                                eerase();
1.31      db        153:                                return (TRUE);
1.64      lum       154:                        }
1.65      jsg       155:                        if (strcasecmp(rep, "no") == 0) {
1.69    ! op        156:                                eerase();
1.31      db        157:                                return (FALSE);
1.64      lum       158:                        }
1.1       deraadt   159:                }
1.36      kjell     160:                rep = eread("Please answer yes or no.  %s? (yes or no) ",
                    161:                    buf, sizeof(buf), EFNUL | EFNEW | EFCR, sp);
1.1       deraadt   162:        }
1.2       millert   163:        /* NOTREACHED */
1.1       deraadt   164: }
1.2       millert   165:
1.1       deraadt   166: /*
1.14      mickey    167:  * This is the general "read input from the echo line" routine.  The basic
1.3       millert   168:  * idea is that the prompt string "prompt" is written to the echo line, and
1.14      mickey    169:  * a one line reply is read back into the supplied "buf" (with maximum
1.36      kjell     170:  * length "len").
                    171:  * XXX: When checking for an empty return value, always check rep, *not* buf
                    172:  * as buf may be freed in pathological cases.
1.1       deraadt   173:  */
1.29      vincent   174: char *
                    175: eread(const char *fmt, char *buf, size_t nbuf, int flag, ...)
1.1       deraadt   176: {
1.3       millert   177:        va_list  ap;
1.31      db        178:        char    *rep;
1.29      vincent   179:
1.2       millert   180:        va_start(ap, flag);
1.29      vincent   181:        rep = veread(fmt, buf, nbuf, flag, ap);
1.2       millert   182:        va_end(ap);
1.31      db        183:        return (rep);
1.1       deraadt   184: }
                    185:
1.29      vincent   186: static char *
                    187: veread(const char *fp, char *buf, size_t nbuf, int flag, va_list ap)
1.2       millert   188: {
1.38      kjell     189:        int      dynbuf = (buf == NULL);
                    190:        int      cpos, epos;            /* cursor, end position in buf */
                    191:        int      c, i, y;
1.62      jasper    192:        int      cplflag;               /* display completion list */
1.38      kjell     193:        int      cwin = FALSE;          /* completion list created */
1.62      jasper    194:        int      mr, ml;                /* match left/right arrows */
                    195:        int      esc;                   /* position in esc pattern */
1.40      deraadt   196:        struct buffer   *bp;                    /* completion list buffer */
                    197:        struct mgwin    *wp;                    /* window for compl list */
1.38      kjell     198:        int      match;                 /* esc match found */
                    199:        int      cc, rr;                /* saved ttcol, ttrow */
                    200:        char    *ret;                   /* return value */
1.1       deraadt   201:
1.47      kjell     202:        static char emptyval[] = "";    /* XXX hackish way to return err msg*/
                    203:
1.2       millert   204:        if (inmacro) {
1.29      vincent   205:                if (dynbuf) {
                    206:                        if ((buf = malloc(maclcur->l_used + 1)) == NULL)
1.31      db        207:                                return (NULL);
1.29      vincent   208:                } else if (maclcur->l_used >= nbuf)
1.31      db        209:                        return (NULL);
1.2       millert   210:                bcopy(maclcur->l_text, buf, maclcur->l_used);
                    211:                buf[maclcur->l_used] = '\0';
                    212:                maclcur = maclcur->l_fp;
1.31      db        213:                return (buf);
1.1       deraadt   214:        }
1.38      kjell     215:        epos = cpos = 0;
                    216:        ml = mr = esc = 0;
                    217:        cplflag = FALSE;
                    218:
1.2       millert   219:        if ((flag & EFNEW) != 0 || ttrow != nrow - 1) {
1.1       deraadt   220:                ttcolor(CTEXT);
1.2       millert   221:                ttmove(nrow - 1, 0);
1.1       deraadt   222:                epresf = TRUE;
                    223:        } else
                    224:                eputc(' ');
                    225:        eformat(fp, ap);
1.16      art       226:        if ((flag & EFDEF) != 0) {
1.29      vincent   227:                if (buf == NULL)
1.31      db        228:                        return (NULL);
1.16      art       229:                eputs(buf);
1.38      kjell     230:                epos = cpos += strlen(buf);
1.16      art       231:        }
1.1       deraadt   232:        tteeol();
                    233:        ttflush();
                    234:        for (;;) {
                    235:                c = getkey(FALSE);
1.51      lum       236:                if ((flag & EFAUTO) != 0 && c == CCHR('I')) {
1.38      kjell     237:                        if (cplflag == TRUE) {
1.41      kjell     238:                                complt_list(flag, buf, cpos);
1.38      kjell     239:                                cwin = TRUE;
                    240:                        } else if (complt(flag, c, buf, nbuf, epos, &i) == TRUE) {
                    241:                                cplflag = TRUE;
                    242:                                epos += i;
                    243:                                cpos = epos;
                    244:                        }
1.1       deraadt   245:                        continue;
                    246:                }
1.38      kjell     247:                cplflag = FALSE;
                    248:
                    249:                if (esc > 0) { /* ESC sequence started */
                    250:                        match = 0;
                    251:                        if (ml == esc && key_left[ml] && c == key_left[ml]) {
                    252:                                match++;
                    253:                                if (key_left[++ml] == '\0') {
                    254:                                        c = CCHR('B');
                    255:                                        esc = 0;
                    256:                                }
                    257:                        }
                    258:                        if (mr == esc && key_right[mr] && c == key_right[mr]) {
                    259:                                match++;
                    260:                                if (key_right[++mr] == '\0') {
                    261:                                        c = CCHR('F');
                    262:                                        esc = 0;
                    263:                                }
                    264:                        }
                    265:                        if (match == 0) {
                    266:                                esc = 0;
                    267:                                continue;
                    268:                                /* hack. how do we know esc pattern is done? */
                    269:                        }
                    270:                        if (esc > 0) {
                    271:                                esc++;
                    272:                                continue;
                    273:                        }
1.1       deraadt   274:                }
1.39      deraadt   275:                switch (c) {
1.38      kjell     276:                case CCHR('A'): /* start of line */
                    277:                        while (cpos > 0) {
                    278:                                if (ISCTRL(buf[--cpos]) != FALSE) {
                    279:                                        ttputc('\b');
                    280:                                        --ttcol;
                    281:                                }
                    282:                                ttputc('\b');
                    283:                                --ttcol;
                    284:                        }
                    285:                        ttflush();
                    286:                        break;
                    287:                case CCHR('D'):
                    288:                        if (cpos != epos) {
                    289:                                tteeol();
                    290:                                epos--;
                    291:                                rr = ttrow;
                    292:                                cc = ttcol;
                    293:                                for (i = cpos; i < epos; i++) {
                    294:                                        buf[i] = buf[i + 1];
                    295:                                        eputc(buf[i]);
                    296:                                }
                    297:                                ttmove(rr, cc);
                    298:                                ttflush();
                    299:                        }
                    300:                        break;
                    301:                case CCHR('E'): /* end of line */
                    302:                        while (cpos < epos) {
                    303:                                eputc(buf[cpos++]);
                    304:                        }
                    305:                        ttflush();
                    306:                        break;
                    307:                case CCHR('B'): /* back */
                    308:                        if (cpos > 0) {
                    309:                                if (ISCTRL(buf[--cpos]) != FALSE) {
                    310:                                        ttputc('\b');
                    311:                                        --ttcol;
                    312:                                }
                    313:                                ttputc('\b');
                    314:                                --ttcol;
                    315:                                ttflush();
                    316:                        }
                    317:                        break;
                    318:                case CCHR('F'): /* forw */
                    319:                        if (cpos < epos) {
                    320:                                eputc(buf[cpos++]);
                    321:                                ttflush();
                    322:                        }
                    323:                        break;
                    324:                case CCHR('Y'): /* yank from kill buffer */
                    325:                        i = 0;
1.67      lum       326:                        while ((y = kremove(i++)) >= 0 && y != *curbp->b_nlchr) {
1.38      kjell     327:                                int t;
                    328:                                if (dynbuf && epos + 1 >= nbuf) {
                    329:                                        void *newp;
                    330:                                        size_t newsize = epos + epos + 16;
                    331:                                        if ((newp = realloc(buf, newsize))
                    332:                                            == NULL)
1.47      kjell     333:                                                goto memfail;
1.38      kjell     334:                                        buf = newp;
                    335:                                        nbuf = newsize;
                    336:                                }
1.47      kjell     337:                                if (!dynbuf && epos + 1 >= nbuf) {
1.56      lum       338:                                        dobeep();
1.68      lum       339:                                        ewprintf("Line too long. Press Control-g to escape.");
                    340:                                        goto skipkey;
1.47      kjell     341:                                }
1.38      kjell     342:                                for (t = epos; t > cpos; t--)
                    343:                                        buf[t] = buf[t - 1];
                    344:                                buf[cpos++] = (char)y;
                    345:                                epos++;
                    346:                                eputc((char)y);
                    347:                                cc = ttcol;
                    348:                                rr = ttrow;
                    349:                                for (t = cpos; t < epos; t++)
                    350:                                        eputc(buf[t]);
                    351:                                ttmove(rr, cc);
                    352:                        }
                    353:                        ttflush();
                    354:                        break;
                    355:                case CCHR('K'): /* copy here-EOL to kill buffer */
                    356:                        kdelete();
                    357:                        for (i = cpos; i < epos; i++)
                    358:                                kinsert(buf[i], KFORW);
                    359:                        tteeol();
                    360:                        epos = cpos;
                    361:                        ttflush();
                    362:                        break;
                    363:                case CCHR('['):
                    364:                        ml = mr = esc = 1;
                    365:                        break;
1.2       millert   366:                case CCHR('J'):
1.3       millert   367:                        c = CCHR('M');
1.22      vincent   368:                        /* FALLTHROUGH */
1.3       millert   369:                case CCHR('M'):                 /* return, done */
1.36      kjell     370:                        /* if there's nothing in the minibuffer, abort */
1.38      kjell     371:                        if (epos == 0 && !(flag & EFNUL)) {
1.33      cloder    372:                                (void)ctrlg(FFRAND, 0);
                    373:                                ttflush();
                    374:                                return (NULL);
                    375:                        }
1.2       millert   376:                        if ((flag & EFFUNC) != 0) {
1.38      kjell     377:                                if (complt(flag, c, buf, nbuf, epos, &i)
                    378:                                    == FALSE)
1.1       deraadt   379:                                        continue;
1.2       millert   380:                                if (i > 0)
1.38      kjell     381:                                        epos += i;
1.1       deraadt   382:                        }
1.38      kjell     383:                        buf[epos] = '\0';
1.2       millert   384:                        if ((flag & EFCR) != 0) {
1.1       deraadt   385:                                ttputc(CCHR('M'));
                    386:                                ttflush();
                    387:                        }
1.2       millert   388:                        if (macrodef) {
1.40      deraadt   389:                                struct line     *lp;
1.1       deraadt   390:
1.47      kjell     391:                                if ((lp = lalloc(cpos)) == NULL)
                    392:                                        goto memfail;
1.2       millert   393:                                lp->l_fp = maclcur->l_fp;
                    394:                                maclcur->l_fp = lp;
                    395:                                lp->l_bp = maclcur;
                    396:                                maclcur = lp;
                    397:                                bcopy(buf, lp->l_text, cpos);
1.1       deraadt   398:                        }
1.38      kjell     399:                        ret = buf;
1.1       deraadt   400:                        goto done;
1.3       millert   401:                case CCHR('G'):                 /* bell, abort */
1.1       deraadt   402:                        eputc(CCHR('G'));
1.10      art       403:                        (void)ctrlg(FFRAND, 0);
1.1       deraadt   404:                        ttflush();
1.38      kjell     405:                        ret = NULL;
                    406:                        goto done;
1.3       millert   407:                case CCHR('H'):                 /* rubout, erase */
1.14      mickey    408:                case CCHR('?'):
1.1       deraadt   409:                        if (cpos != 0) {
1.38      kjell     410:                                y = buf[--cpos];
                    411:                                epos--;
1.1       deraadt   412:                                ttputc('\b');
1.38      kjell     413:                                ttcol--;
                    414:                                if (ISCTRL(y) != FALSE) {
                    415:                                        ttputc('\b');
                    416:                                        ttcol--;
                    417:                                }
                    418:                                rr = ttrow;
                    419:                                cc = ttcol;
                    420:                                for (i = cpos; i < epos; i++) {
                    421:                                        buf[i] = buf[i + 1];
                    422:                                        eputc(buf[i]);
                    423:                                }
1.1       deraadt   424:                                ttputc(' ');
1.38      kjell     425:                                if (ISCTRL(y) != FALSE) {
1.1       deraadt   426:                                        ttputc(' ');
                    427:                                        ttputc('\b');
                    428:                                }
1.38      kjell     429:                                ttputc('\b');
                    430:                                ttmove(rr, cc);
1.1       deraadt   431:                                ttflush();
                    432:                        }
                    433:                        break;
1.3       millert   434:                case CCHR('X'):                 /* kill line */
                    435:                case CCHR('U'):
1.1       deraadt   436:                        while (cpos != 0) {
                    437:                                ttputc('\b');
                    438:                                ttputc(' ');
                    439:                                ttputc('\b');
                    440:                                --ttcol;
                    441:                                if (ISCTRL(buf[--cpos]) != FALSE) {
                    442:                                        ttputc('\b');
                    443:                                        ttputc(' ');
                    444:                                        ttputc('\b');
                    445:                                        --ttcol;
                    446:                                }
1.38      kjell     447:                                epos--;
1.1       deraadt   448:                        }
                    449:                        ttflush();
                    450:                        break;
1.3       millert   451:                case CCHR('W'):                 /* kill to beginning of word */
1.1       deraadt   452:                        while ((cpos > 0) && !ISWORD(buf[cpos - 1])) {
                    453:                                ttputc('\b');
                    454:                                ttputc(' ');
                    455:                                ttputc('\b');
                    456:                                --ttcol;
                    457:                                if (ISCTRL(buf[--cpos]) != FALSE) {
                    458:                                        ttputc('\b');
                    459:                                        ttputc(' ');
                    460:                                        ttputc('\b');
                    461:                                        --ttcol;
                    462:                                }
1.46      kjell     463:                                epos--;
1.1       deraadt   464:                        }
                    465:                        while ((cpos > 0) && ISWORD(buf[cpos - 1])) {
                    466:                                ttputc('\b');
                    467:                                ttputc(' ');
                    468:                                ttputc('\b');
                    469:                                --ttcol;
                    470:                                if (ISCTRL(buf[--cpos]) != FALSE) {
                    471:                                        ttputc('\b');
                    472:                                        ttputc(' ');
                    473:                                        ttputc('\b');
                    474:                                        --ttcol;
                    475:                                }
1.46      kjell     476:                                epos--;
1.1       deraadt   477:                        }
                    478:                        ttflush();
                    479:                        break;
1.2       millert   480:                case CCHR('\\'):
1.3       millert   481:                case CCHR('Q'):                 /* quote next */
                    482:                        c = getkey(FALSE);
1.22      vincent   483:                        /* FALLTHROUGH */
1.30      deraadt   484:                default:
1.38      kjell     485:                        if (dynbuf && epos + 1 >= nbuf) {
1.29      vincent   486:                                void *newp;
1.38      kjell     487:                                size_t newsize = epos + epos + 16;
                    488:                                if ((newp = realloc(buf, newsize)) == NULL)
1.47      kjell     489:                                        goto memfail;
1.29      vincent   490:                                buf = newp;
                    491:                                nbuf = newsize;
                    492:                        }
1.47      kjell     493:                        if (!dynbuf && epos + 1 >= nbuf) {
1.56      lum       494:                                dobeep();
1.68      lum       495:                                ewprintf("Line too long. Press Control-g to escape.");
                    496:                                goto skipkey;
1.47      kjell     497:                        }
1.38      kjell     498:                        for (i = epos; i > cpos; i--)
                    499:                                buf[i] = buf[i - 1];
                    500:                        buf[cpos++] = (char)c;
                    501:                        epos++;
                    502:                        eputc((char)c);
                    503:                        cc = ttcol;
                    504:                        rr = ttrow;
                    505:                        for (i = cpos; i < epos; i++)
                    506:                                eputc(buf[i]);
                    507:                        ttmove(rr, cc);
                    508:                        ttflush();
1.1       deraadt   509:                }
1.68      lum       510:
                    511: skipkey:       /* ignore key press */
                    512: ;
1.1       deraadt   513:        }
1.7       art       514: done:
1.38      kjell     515:        if (cwin == TRUE) {
                    516:                /* blow away cpltion window */
                    517:                bp = bfind("*Completions*", TRUE);
1.49      kjell     518:                if ((wp = popbuf(bp, WEPHEM)) != NULL) {
                    519:                        if (wp->w_flag & WEPHEM) {
                    520:                                curwp = wp;
                    521:                                delwind(FFRAND, 1);
                    522:                        } else {
                    523:                                killbuffer(bp);
                    524:                        }
1.38      kjell     525:                }
                    526:        }
                    527:        return (ret);
1.47      kjell     528: memfail:
                    529:        if (dynbuf && buf)
                    530:                free(buf);
1.56      lum       531:        dobeep();
1.38      kjell     532:        ewprintf("Out of memory");
1.47      kjell     533:        return (emptyval);
1.1       deraadt   534: }
                    535:
                    536: /*
1.31      db        537:  * Do completion on a list of objects.
1.38      kjell     538:  * c is SPACE, TAB, or CR
                    539:  * return TRUE if matched (or partially matched)
                    540:  * FALSE is result is ambiguous,
                    541:  * ABORT on error.
1.1       deraadt   542:  */
1.2       millert   543: static int
1.38      kjell     544: complt(int flags, int c, char *buf, size_t nbuf, int cpos, int *nx)
1.3       millert   545: {
1.40      deraadt   546:        struct list     *lh, *lh2;
                    547:        struct list     *wholelist = NULL;
1.3       millert   548:        int      i, nxtra, nhits, bxtra, msglen, nshown;
                    549:        int      wflag = FALSE;
                    550:        char    *msg;
                    551:
                    552:        lh = lh2 = NULL;
1.2       millert   553:
                    554:        if ((flags & EFFUNC) != 0) {
                    555:                buf[cpos] = '\0';
1.32      otto      556:                wholelist = lh = complete_function_list(buf);
1.9       art       557:        } else if ((flags & EFBUF) != 0) {
1.6       art       558:                lh = &(bheadp->b_list);
1.9       art       559:        } else if ((flags & EFFILE) != 0) {
1.6       art       560:                buf[cpos] = '\0';
                    561:                wholelist = lh = make_file_list(buf);
                    562:        } else
                    563:                panic("broken complt call: flags");
                    564:
                    565:        if (c == ' ')
                    566:                wflag = TRUE;
                    567:        else if (c != '\t' && c != CCHR('M'))
                    568:                panic("broken complt call: c");
                    569:
                    570:        nhits = 0;
                    571:        nxtra = HUGE;
                    572:
                    573:        for (; lh != NULL; lh = lh->l_next) {
                    574:                if (memcmp(buf, lh->l_name, cpos) != 0)
                    575:                        continue;
1.2       millert   576:                if (nhits == 0)
1.6       art       577:                        lh2 = lh;
                    578:                ++nhits;
                    579:                if (lh->l_name[cpos] == '\0')
1.38      kjell     580:                        nxtra = -1; /* exact match */
1.3       millert   581:                else {
1.6       art       582:                        bxtra = getxtra(lh, lh2, cpos, wflag);
                    583:                        if (bxtra < nxtra)
                    584:                                nxtra = bxtra;
                    585:                        lh2 = lh;
                    586:                }
                    587:        }
                    588:        if (nhits == 0)
                    589:                msg = " [No match]";
                    590:        else if (nhits > 1 && nxtra == 0)
1.37      kjell     591:                msg = " [Ambiguous. Ctrl-G to cancel]";
1.6       art       592:        else {
                    593:                /*
                    594:                 * Being lazy - ought to check length, but all things
                    595:                 * autocompleted have known types/lengths.
                    596:                 */
                    597:                if (nxtra < 0 && nhits > 1 && c == ' ')
1.38      kjell     598:                        nxtra = 1; /* ??? */
1.29      vincent   599:                for (i = 0; i < nxtra && cpos < nbuf; ++i) {
1.6       art       600:                        buf[cpos] = lh2->l_name[cpos];
                    601:                        eputc(buf[cpos++]);
1.1       deraadt   602:                }
1.38      kjell     603:                /* XXX should grow nbuf */
1.6       art       604:                ttflush();
                    605:                free_file_list(wholelist);
1.38      kjell     606:                *nx = nxtra;
                    607:                if (nxtra < 0 && c != CCHR('M')) /* exact */
                    608:                        *nx = 0;
                    609:                return (TRUE);
1.1       deraadt   610:        }
1.3       millert   611:
1.2       millert   612:        /*
1.31      db        613:         * wholelist is NULL if we are doing buffers.  Want to free lists
1.2       millert   614:         * that were created for us, but not the buffer list!
                    615:         */
1.1       deraadt   616:        free_file_list(wholelist);
1.3       millert   617:
1.31      db        618:        /* Set up backspaces, etc., being mindful of echo line limit. */
1.1       deraadt   619:        msglen = strlen(msg);
                    620:        nshown = (ttcol + msglen + 2 > ncol) ?
1.2       millert   621:                ncol - ttcol - 2 : msglen;
1.1       deraadt   622:        eputs(msg);
1.2       millert   623:        ttcol -= (i = nshown);  /* update ttcol!                 */
                    624:        while (i--)             /* move back before msg          */
1.1       deraadt   625:                ttputc('\b');
1.2       millert   626:        ttflush();              /* display to user               */
1.1       deraadt   627:        i = nshown;
1.3       millert   628:        while (i--)             /* blank out on next flush       */
1.1       deraadt   629:                eputc(' ');
1.2       millert   630:        ttcol -= (i = nshown);  /* update ttcol on BS's          */
1.1       deraadt   631:        while (i--)
1.2       millert   632:                ttputc('\b');   /* update ttcol again!           */
1.38      kjell     633:        *nx = nxtra;
                    634:        return ((nhits > 0) ? TRUE : FALSE);
1.1       deraadt   635: }
                    636:
                    637: /*
1.31      db        638:  * Do completion on a list of objects, listing instead of completing.
1.1       deraadt   639:  */
1.2       millert   640: static int
1.41      kjell     641: complt_list(int flags, char *buf, int cpos)
1.3       millert   642: {
1.40      deraadt   643:        struct list     *lh, *lh2, *lh3;
                    644:        struct list     *wholelist = NULL;
                    645:        struct buffer   *bp;
1.3       millert   646:        int      i, maxwidth, width;
                    647:        int      preflen = 0;
                    648:        int      oldrow = ttrow;
                    649:        int      oldcol = ttcol;
                    650:        int      oldhue = tthue;
1.17      art       651:        char     *linebuf;
1.26      millert   652:        size_t   linesize, len;
1.43      kjell     653:        char *cp;
1.3       millert   654:
                    655:        lh = NULL;
1.1       deraadt   656:
                    657:        ttflush();
                    658:
1.38      kjell     659:        /* The results are put into a completion buffer. */
                    660:        bp = bfind("*Completions*", TRUE);
1.2       millert   661:        if (bclear(bp) == FALSE)
1.31      db        662:                return (FALSE);
1.61      jasper    663:        bp->b_flag |= BFREADONLY;
1.2       millert   664:
1.24      vincent   665:        /*
1.31      db        666:         * First get the list of objects.  This list may contain only
1.24      vincent   667:         * the ones that complete what has been typed, or may be the
                    668:         * whole list of all objects of this type.  They are filtered
                    669:         * later in any case.  Set wholelist if the list has been
                    670:         * cons'ed up just for us, so we can free it later.  We have
                    671:         * to copy the buffer list for this function even though we
                    672:         * didn't for complt.  The sorting code does destructive
                    673:         * changes to the list, which we don't want to happen to the
                    674:         * main buffer list!
                    675:         */
                    676:        if ((flags & EFBUF) != 0)
                    677:                wholelist = lh = copy_list(&(bheadp->b_list));
                    678:        else if ((flags & EFFUNC) != 0) {
                    679:                buf[cpos] = '\0';
1.32      otto      680:                wholelist = lh = complete_function_list(buf);
1.24      vincent   681:        } else if ((flags & EFFILE) != 0) {
                    682:                buf[cpos] = '\0';
                    683:                wholelist = lh = make_file_list(buf);
1.2       millert   684:                /*
1.24      vincent   685:                 * We don't want to display stuff up to the / for file
                    686:                 * names preflen is the list of a prefix of what the
                    687:                 * user typed that should not be displayed.
1.2       millert   688:                 */
1.24      vincent   689:                cp = strrchr(buf, '/');
                    690:                if (cp)
                    691:                        preflen = cp - buf + 1;
                    692:        } else
                    693:                panic("broken complt call: flags");
1.1       deraadt   694:
1.24      vincent   695:        /*
                    696:         * Sort the list, since users expect to see it in alphabetic
                    697:         * order.
                    698:         */
                    699:        lh2 = lh;
                    700:        while (lh2 != NULL) {
                    701:                lh3 = lh2->l_next;
                    702:                while (lh3 != NULL) {
                    703:                        if (strcmp(lh2->l_name, lh3->l_name) > 0) {
                    704:                                cp = lh2->l_name;
                    705:                                lh2->l_name = lh3->l_name;
                    706:                                lh3->l_name = cp;
1.2       millert   707:                        }
1.24      vincent   708:                        lh3 = lh3->l_next;
1.2       millert   709:                }
1.24      vincent   710:                lh2 = lh2->l_next;
                    711:        }
1.1       deraadt   712:
1.24      vincent   713:        /*
                    714:         * First find max width of object to be displayed, so we can
                    715:         * put several on a line.
                    716:         */
                    717:        maxwidth = 0;
                    718:        lh2 = lh;
                    719:        while (lh2 != NULL) {
                    720:                for (i = 0; i < cpos; ++i) {
                    721:                        if (buf[i] != lh2->l_name[i])
                    722:                                break;
                    723:                }
                    724:                if (i == cpos) {
                    725:                        width = strlen(lh2->l_name);
                    726:                        if (width > maxwidth)
                    727:                                maxwidth = width;
1.1       deraadt   728:                }
1.24      vincent   729:                lh2 = lh2->l_next;
                    730:        }
                    731:        maxwidth += 1 - preflen;
1.2       millert   732:
1.24      vincent   733:        /*
1.31      db        734:         * Now do the display.  Objects are written into linebuf until
1.24      vincent   735:         * it fills, and then put into the help buffer.
                    736:         */
1.57      guenther  737:        linesize = (ncol > maxwidth ? ncol : maxwidth) + 1;
1.54      florian   738:        if ((linebuf = malloc(linesize)) == NULL) {
                    739:                free_file_list(wholelist);
1.31      db        740:                return (FALSE);
1.54      florian   741:        }
1.24      vincent   742:        width = 0;
1.19      vincent   743:
1.25      deraadt   744:        /*
1.24      vincent   745:         * We're going to strlcat() into the buffer, so it has to be
1.31      db        746:         * NUL terminated.
1.24      vincent   747:         */
                    748:        linebuf[0] = '\0';
                    749:        for (lh2 = lh; lh2 != NULL; lh2 = lh2->l_next) {
                    750:                for (i = 0; i < cpos; ++i) {
                    751:                        if (buf[i] != lh2->l_name[i])
                    752:                                break;
1.2       millert   753:                }
1.24      vincent   754:                /* if we have a match */
                    755:                if (i == cpos) {
                    756:                        /* if it wraps */
                    757:                        if ((width + maxwidth) > ncol) {
                    758:                                addline(bp, linebuf);
                    759:                                linebuf[0] = '\0';
                    760:                                width = 0;
                    761:                        }
1.43      kjell     762:                        len = strlcat(linebuf, lh2->l_name + preflen,
                    763:                            linesize);
1.24      vincent   764:                        width += maxwidth;
1.26      millert   765:                        if (len < width && width < linesize) {
                    766:                                /* pad so the objects nicely line up */
                    767:                                memset(linebuf + len, ' ',
                    768:                                    maxwidth - strlen(lh2->l_name + preflen));
                    769:                                linebuf[width] = '\0';
                    770:                        }
1.1       deraadt   771:                }
1.2       millert   772:        }
1.24      vincent   773:        if (width > 0)
                    774:                addline(bp, linebuf);
                    775:        free(linebuf);
                    776:
1.2       millert   777:        /*
                    778:         * Note that we free lists only if they are put in wholelist lists
                    779:         * that were built just for us should be freed.  However when we use
                    780:         * the buffer list, obviously we don't want it freed.
1.1       deraadt   781:         */
                    782:        free_file_list(wholelist);
1.49      kjell     783:        popbuftop(bp, WEPHEM);  /* split the screen and put up the help
1.2       millert   784:                                 * buffer */
1.55      lum       785:        update(CMODE);          /* needed to make the new stuff actually
1.2       millert   786:                                 * appear */
                    787:        ttmove(oldrow, oldcol); /* update leaves cursor in arbitrary place */
                    788:        ttcolor(oldhue);        /* with arbitrary color */
1.1       deraadt   789:        ttflush();
1.31      db        790:        return (0);
1.1       deraadt   791: }
                    792:
                    793: /*
1.3       millert   794:  * The "lp1" and "lp2" point to list structures.  The "cpos" is a horizontal
1.14      mickey    795:  * position in the name.  Return the longest block of characters that can be
                    796:  * autocompleted at this point.  Sometimes the two symbols are the same, but
1.3       millert   797:  * this is normal.
1.2       millert   798:  */
                    799: int
1.40      deraadt   800: getxtra(struct list *lp1, struct list *lp2, int cpos, int wflag)
1.2       millert   801: {
1.3       millert   802:        int     i;
1.1       deraadt   803:
                    804:        i = cpos;
                    805:        for (;;) {
1.2       millert   806:                if (lp1->l_name[i] != lp2->l_name[i])
                    807:                        break;
                    808:                if (lp1->l_name[i] == '\0')
                    809:                        break;
1.1       deraadt   810:                ++i;
1.2       millert   811:                if (wflag && !ISWORD(lp1->l_name[i - 1]))
                    812:                        break;
1.1       deraadt   813:        }
                    814:        return (i - cpos);
                    815: }
                    816:
                    817: /*
1.14      mickey    818:  * Special "printf" for the echo line.  Each call to "ewprintf" starts a
                    819:  * new line in the echo area, and ends with an erase to end of the echo
                    820:  * line.  The formatting is done by a call to the standard formatting
1.3       millert   821:  * routine.
1.1       deraadt   822:  */
1.10      art       823: void
1.2       millert   824: ewprintf(const char *fmt, ...)
1.1       deraadt   825: {
1.3       millert   826:        va_list  ap;
1.1       deraadt   827:
1.2       millert   828:        if (inmacro)
                    829:                return;
1.50      lum       830:
1.2       millert   831:        va_start(ap, fmt);
1.1       deraadt   832:        ttcolor(CTEXT);
1.2       millert   833:        ttmove(nrow - 1, 0);
                    834:        eformat(fmt, ap);
                    835:        va_end(ap);
1.1       deraadt   836:        tteeol();
                    837:        ttflush();
                    838:        epresf = TRUE;
                    839: }
                    840:
                    841: /*
1.59      bcallah   842:  * Printf style formatting. This is called by "ewprintf" to provide
                    843:  * formatting services to its clients.  The move to the start of the
                    844:  * echo line, and the erase to the end of the echo line, is done by
1.48      kjell     845:  * the caller.
                    846:  * %c prints the "name" of the supplied character.
                    847:  * %k prints the name of the current key (and takes no arguments).
                    848:  * %d prints a decimal integer
                    849:  * %o prints an octal integer
                    850:  * %p prints a pointer
                    851:  * %s prints a string
                    852:  * %ld prints a long word
                    853:  * Anything else is echoed verbatim
1.1       deraadt   854:  */
1.10      art       855: static void
1.15      mickey    856: eformat(const char *fp, va_list ap)
1.1       deraadt   857: {
1.27      vincent   858:        char    kname[NKNAME], tmp[100], *cp;
1.12      mickey    859:        int     c;
1.1       deraadt   860:
                    861:        while ((c = *fp++) != '\0') {
                    862:                if (c != '%')
                    863:                        eputc(c);
                    864:                else {
                    865:                        c = *fp++;
                    866:                        switch (c) {
                    867:                        case 'c':
1.48      kjell     868:                                getkeyname(kname, sizeof(kname),
                    869:                                    va_arg(ap, int));
1.1       deraadt   870:                                eputs(kname);
                    871:                                break;
                    872:
                    873:                        case 'k':
1.12      mickey    874:                                for (cp = kname, c = 0; c < key.k_count; c++) {
                    875:                                        if (c)
                    876:                                                *cp++ = ' ';
1.42      kjell     877:                                        cp = getkeyname(cp, sizeof(kname) -
1.12      mickey    878:                                            (cp - kname) - 1, key.k_chars[c]);
1.1       deraadt   879:                                }
                    880:                                eputs(kname);
                    881:                                break;
                    882:
                    883:                        case 'd':
1.2       millert   884:                                eputi(va_arg(ap, int), 10);
1.1       deraadt   885:                                break;
                    886:
                    887:                        case 'o':
1.2       millert   888:                                eputi(va_arg(ap, int), 8);
1.27      vincent   889:                                break;
                    890:
                    891:                        case 'p':
1.31      db        892:                                snprintf(tmp, sizeof(tmp), "%p",
1.27      vincent   893:                                    va_arg(ap, void *));
                    894:                                eputs(tmp);
1.1       deraadt   895:                                break;
                    896:
                    897:                        case 's':
1.2       millert   898:                                eputs(va_arg(ap, char *));
1.1       deraadt   899:                                break;
                    900:
1.3       millert   901:                        case 'l':
                    902:                                /* explicit longword */
1.1       deraadt   903:                                c = *fp++;
1.2       millert   904:                                switch (c) {
1.1       deraadt   905:                                case 'd':
1.13      mickey    906:                                        eputl(va_arg(ap, long), 10);
1.1       deraadt   907:                                        break;
                    908:                                default:
                    909:                                        eputc(c);
                    910:                                        break;
                    911:                                }
                    912:                                break;
                    913:
                    914:                        default:
                    915:                                eputc(c);
                    916:                        }
                    917:                }
                    918:        }
                    919: }
                    920:
                    921: /*
                    922:  * Put integer, in radix "r".
                    923:  */
1.10      art       924: static void
1.21      vincent   925: eputi(int i, int r)
1.1       deraadt   926: {
1.3       millert   927:        int      q;
1.1       deraadt   928:
1.2       millert   929:        if (i < 0) {
                    930:                eputc('-');
                    931:                i = -i;
1.1       deraadt   932:        }
1.2       millert   933:        if ((q = i / r) != 0)
1.1       deraadt   934:                eputi(q, r);
1.2       millert   935:        eputc(i % r + '0');
1.1       deraadt   936: }
                    937:
                    938: /*
                    939:  * Put long, in radix "r".
                    940:  */
1.10      art       941: static void
1.21      vincent   942: eputl(long l, int r)
1.1       deraadt   943: {
1.3       millert   944:        long     q;
1.1       deraadt   945:
1.2       millert   946:        if (l < 0) {
                    947:                eputc('-');
                    948:                l = -l;
1.1       deraadt   949:        }
1.2       millert   950:        if ((q = l / r) != 0)
1.1       deraadt   951:                eputl(q, r);
1.3       millert   952:        eputc((int)(l % r) + '0');
1.1       deraadt   953: }
                    954:
                    955: /*
                    956:  * Put string.
                    957:  */
1.10      art       958: static void
1.21      vincent   959: eputs(const char *s)
1.1       deraadt   960: {
1.3       millert   961:        int      c;
1.1       deraadt   962:
                    963:        while ((c = *s++) != '\0')
                    964:                eputc(c);
                    965: }
                    966:
                    967: /*
1.14      mickey    968:  * Put character.  Watch for control characters, and for the line getting
1.3       millert   969:  * too long.
1.1       deraadt   970:  */
1.10      art       971: static void
1.21      vincent   972: eputc(char c)
1.1       deraadt   973: {
1.2       millert   974:        if (ttcol + 2 < ncol) {
1.1       deraadt   975:                if (ISCTRL(c)) {
                    976:                        eputc('^');
                    977:                        c = CCHR(c);
                    978:                }
                    979:                ttputc(c);
                    980:                ++ttcol;
                    981:        }
                    982: }
                    983:
1.10      art       984: void
1.40      deraadt   985: free_file_list(struct list *lp)
1.1       deraadt   986: {
1.40      deraadt   987:        struct list     *next;
1.2       millert   988:
                    989:        while (lp) {
                    990:                next = lp->l_next;
1.43      kjell     991:                free(lp->l_name);
1.2       millert   992:                free(lp);
                    993:                lp = next;
                    994:        }
1.1       deraadt   995: }
                    996:
1.40      deraadt   997: static struct list *
                    998: copy_list(struct list *lp)
1.2       millert   999: {
1.40      deraadt  1000:        struct list     *current, *last, *nxt;
1.2       millert  1001:
                   1002:        last = NULL;
                   1003:        while (lp) {
1.45      kjell    1004:                current = malloc(sizeof(struct list));
1.23      vincent  1005:                if (current == NULL) {
1.43      kjell    1006:                        /* Free what we have allocated so far */
1.23      vincent  1007:                        for (current = last; current; current = nxt) {
                   1008:                                nxt = current->l_next;
1.43      kjell    1009:                                free(current->l_name);
1.23      vincent  1010:                                free(current);
                   1011:                        }
1.28      vincent  1012:                        return (NULL);
1.23      vincent  1013:                }
1.2       millert  1014:                current->l_next = last;
1.43      kjell    1015:                current->l_name = strdup(lp->l_name);
1.28      vincent  1016:                last = current;
1.2       millert  1017:                lp = lp->l_next;
                   1018:        }
                   1019:        return (last);
1.1       deraadt  1020: }