[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.41

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