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