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