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